/// Types for Synor Storage SDK.
library synor_storage_types;

import 'dart:typed_data';

/// Status of a pinned item.
enum PinStatus {
  queued,
  pinning,
  pinned,
  failed,
  unpinned;

  String toJson() => name;

  static PinStatus fromJson(String json) {
    return PinStatus.values.firstWhere(
      (e) => e.name == json,
      orElse: () => PinStatus.queued,
    );
  }
}

/// Hash algorithm for content addressing.
enum HashAlgorithm {
  sha2_256('sha2-256'),
  blake3('blake3');

  final String value;
  const HashAlgorithm(this.value);

  String toJson() => value;

  static HashAlgorithm fromJson(String json) {
    return HashAlgorithm.values.firstWhere(
      (e) => e.value == json,
      orElse: () => HashAlgorithm.sha2_256,
    );
  }
}

/// Type of directory entry.
enum EntryType {
  file,
  directory;

  String toJson() => name;

  static EntryType fromJson(String json) {
    return EntryType.values.firstWhere(
      (e) => e.name == json,
      orElse: () => EntryType.file,
    );
  }
}

/// Configuration for the storage client.
class StorageConfig {
  final String apiKey;
  final String endpoint;
  final String gateway;
  final Duration timeout;
  final int retries;
  final bool debug;

  const StorageConfig({
    required this.apiKey,
    this.endpoint = 'https://storage.synor.io/v1',
    this.gateway = 'https://gateway.synor.io',
    this.timeout = const Duration(seconds: 60),
    this.retries = 3,
    this.debug = false,
  });
}

/// Options for file upload.
class UploadOptions {
  final String? name;
  final bool wrapWithDirectory;
  final HashAlgorithm hashAlgorithm;
  final int? chunkSize;
  final int? pinDurationDays;

  const UploadOptions({
    this.name,
    this.wrapWithDirectory = false,
    this.hashAlgorithm = HashAlgorithm.sha2_256,
    this.chunkSize,
    this.pinDurationDays,
  });
}

/// Response from upload operation.
class UploadResponse {
  final String cid;
  final int size;
  final String? name;
  final String createdAt;

  const UploadResponse({
    required this.cid,
    required this.size,
    this.name,
    required this.createdAt,
  });

  factory UploadResponse.fromJson(Map<String, dynamic> json) {
    return UploadResponse(
      cid: json['cid'] as String,
      size: json['size'] as int,
      name: json['name'] as String?,
      createdAt: json['created_at'] as String,
    );
  }
}

/// Pin information.
class Pin {
  final String cid;
  final String? name;
  final PinStatus status;
  final int size;
  final String createdAt;
  final String? expiresAt;
  final List<String>? delegates;

  const Pin({
    required this.cid,
    this.name,
    required this.status,
    required this.size,
    required this.createdAt,
    this.expiresAt,
    this.delegates,
  });

  factory Pin.fromJson(Map<String, dynamic> json) {
    return Pin(
      cid: json['cid'] as String,
      name: json['name'] as String?,
      status: PinStatus.fromJson(json['status'] as String),
      size: json['size'] as int,
      createdAt: json['created_at'] as String,
      expiresAt: json['expires_at'] as String?,
      delegates: (json['delegates'] as List?)?.cast<String>(),
    );
  }
}

/// Request to pin content.
class PinRequest {
  final String cid;
  final String? name;
  final int? durationDays;
  final List<String>? origins;
  final Map<String, String>? meta;

  const PinRequest({
    required this.cid,
    this.name,
    this.durationDays,
    this.origins,
    this.meta,
  });

  Map<String, dynamic> toJson() => {
        'cid': cid,
        if (name != null) 'name': name,
        if (durationDays != null) 'duration_days': durationDays,
        if (origins != null) 'origins': origins,
        if (meta != null) 'meta': meta,
      };
}

/// CAR (Content Addressable Archive) file.
class CarFile {
  final String cid;
  final int size;
  final List<String> roots;
  final String createdAt;

  const CarFile({
    required this.cid,
    required this.size,
    required this.roots,
    required this.createdAt,
  });

  factory CarFile.fromJson(Map<String, dynamic> json) {
    return CarFile(
      cid: json['cid'] as String,
      size: json['size'] as int,
      roots: (json['roots'] as List).cast<String>(),
      createdAt: json['created_at'] as String,
    );
  }
}

/// Entry in a CAR file for creation.
class CarEntry {
  final String path;
  final Uint8List content;

  const CarEntry({
    required this.path,
    required this.content,
  });
}

/// Directory entry.
class DirectoryEntry {
  final String name;
  final String cid;
  final int size;
  final EntryType type;

  const DirectoryEntry({
    required this.name,
    required this.cid,
    required this.size,
    required this.type,
  });

  factory DirectoryEntry.fromJson(Map<String, dynamic> json) {
    return DirectoryEntry(
      name: json['name'] as String,
      cid: json['cid'] as String,
      size: json['size'] as int,
      type: EntryType.fromJson(json['type'] as String),
    );
  }
}

/// File entry for directory creation.
class FileEntry {
  final String path;
  final Uint8List content;

  const FileEntry({
    required this.path,
    required this.content,
  });
}

/// Storage usage statistics.
class StorageStats {
  final int totalSize;
  final int pinCount;
  final int bandwidthUsed;
  final double quotaUsed;

  const StorageStats({
    required this.totalSize,
    required this.pinCount,
    required this.bandwidthUsed,
    required this.quotaUsed,
  });

  factory StorageStats.fromJson(Map<String, dynamic> json) {
    return StorageStats(
      totalSize: json['total_size'] as int,
      pinCount: json['pin_count'] as int,
      bandwidthUsed: json['bandwidth_used'] as int,
      quotaUsed: (json['quota_used'] as num).toDouble(),
    );
  }
}

/// Exception thrown by storage operations.
class StorageException implements Exception {
  final String message;
  final String? code;
  final int? statusCode;

  const StorageException(this.message, {this.code, this.statusCode});

  @override
  String toString() => 'StorageException: $message';
}
