//! Synor Storage SDK Types.

use serde::{Deserialize, Serialize};

/// Pin status.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum PinStatus {
    Queued,
    Pinning,
    Pinned,
    Failed,
    Unpinned,
}

/// Hash algorithm.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum HashAlgorithm {
    #[default]
    #[serde(rename = "sha2-256")]
    Sha256,
    #[serde(rename = "blake3")]
    Blake3,
}

/// Entry type.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum EntryType {
    File,
    Directory,
}

/// Match type for listing pins.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum MatchType {
    Exact,
    IExact,
    Partial,
    IPartial,
}

/// Storage SDK configuration.
#[derive(Debug, Clone)]
pub struct StorageConfig {
    pub api_key: String,
    pub endpoint: String,
    pub gateway: String,
    pub pinning_service: Option<String>,
    pub chunk_size: usize,
    pub timeout_secs: u64,
    pub debug: bool,
}

impl StorageConfig {
    /// Create a new configuration with the given API key.
    pub fn new(api_key: impl Into<String>) -> Self {
        Self {
            api_key: api_key.into(),
            endpoint: "https://storage.synor.cc/api/v1".to_string(),
            gateway: "https://gateway.synor.cc".to_string(),
            pinning_service: None,
            chunk_size: 262144, // 256KB
            timeout_secs: 30,
            debug: false,
        }
    }

    /// Set the endpoint.
    pub fn endpoint(mut self, endpoint: impl Into<String>) -> Self {
        self.endpoint = endpoint.into();
        self
    }

    /// Set the gateway.
    pub fn gateway(mut self, gateway: impl Into<String>) -> Self {
        self.gateway = gateway.into();
        self
    }

    /// Set the pinning service.
    pub fn pinning_service(mut self, service: impl Into<String>) -> Self {
        self.pinning_service = Some(service.into());
        self
    }

    /// Set the chunk size.
    pub fn chunk_size(mut self, size: usize) -> Self {
        self.chunk_size = size;
        self
    }

    /// Set the timeout in seconds.
    pub fn timeout(mut self, secs: u64) -> Self {
        self.timeout_secs = secs;
        self
    }

    /// Enable debug mode.
    pub fn debug(mut self, debug: bool) -> Self {
        self.debug = debug;
        self
    }
}

/// Upload options.
#[derive(Debug, Clone, Default)]
pub struct UploadOptions {
    pub pin: bool,
    pub wrap_with_directory: bool,
    pub cid_version: u8,
    pub hash_algorithm: HashAlgorithm,
}

impl UploadOptions {
    /// Create new upload options with pinning enabled.
    pub fn new() -> Self {
        Self {
            pin: true,
            cid_version: 1,
            ..Default::default()
        }
    }

    /// Set whether to pin content.
    pub fn pin(mut self, pin: bool) -> Self {
        self.pin = pin;
        self
    }

    /// Set whether to wrap with directory.
    pub fn wrap_with_directory(mut self, wrap: bool) -> Self {
        self.wrap_with_directory = wrap;
        self
    }

    /// Set the CID version.
    pub fn cid_version(mut self, version: u8) -> Self {
        self.cid_version = version;
        self
    }

    /// Set the hash algorithm.
    pub fn hash_algorithm(mut self, algorithm: HashAlgorithm) -> Self {
        self.hash_algorithm = algorithm;
        self
    }
}

/// Upload response.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UploadResponse {
    pub cid: String,
    pub size: i64,
    pub name: Option<String>,
    pub hash: Option<String>,
}

/// Download options.
#[derive(Debug, Clone, Default)]
pub struct DownloadOptions {
    pub offset: Option<i64>,
    pub length: Option<i64>,
}

impl DownloadOptions {
    /// Create new download options.
    pub fn new() -> Self {
        Self::default()
    }

    /// Set the offset.
    pub fn offset(mut self, offset: i64) -> Self {
        self.offset = Some(offset);
        self
    }

    /// Set the length.
    pub fn length(mut self, length: i64) -> Self {
        self.length = Some(length);
        self
    }
}

/// Pin information.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Pin {
    pub cid: String,
    pub status: PinStatus,
    pub name: Option<String>,
    pub size: Option<i64>,
    pub created_at: Option<i64>,
    pub expires_at: Option<i64>,
    #[serde(default)]
    pub delegates: Vec<String>,
}

/// Pin request.
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PinRequest {
    pub cid: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub duration: Option<i64>,
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub origins: Vec<String>,
}

impl PinRequest {
    /// Create a new pin request.
    pub fn new(cid: impl Into<String>) -> Self {
        Self {
            cid: cid.into(),
            name: None,
            duration: None,
            origins: Vec::new(),
        }
    }

    /// Set the name.
    pub fn name(mut self, name: impl Into<String>) -> Self {
        self.name = Some(name.into());
        self
    }

    /// Set the duration.
    pub fn duration(mut self, duration: i64) -> Self {
        self.duration = Some(duration);
        self
    }

    /// Set the origins.
    pub fn origins(mut self, origins: Vec<String>) -> Self {
        self.origins = origins;
        self
    }
}

/// List pins options.
#[derive(Debug, Clone, Default)]
pub struct ListPinsOptions {
    pub status: Vec<PinStatus>,
    pub match_type: Option<MatchType>,
    pub name: Option<String>,
    pub limit: Option<i32>,
    pub offset: Option<i32>,
}

/// List pins response.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ListPinsResponse {
    pub pins: Vec<Pin>,
    pub total: i32,
    pub has_more: bool,
}

/// Gateway URL.
#[derive(Debug, Clone)]
pub struct GatewayUrl {
    pub url: String,
    pub cid: String,
    pub path: Option<String>,
}

/// CAR block.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CarBlock {
    pub cid: String,
    pub data: String, // Base64-encoded
    pub size: Option<i64>,
}

/// CAR file.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CarFile {
    pub version: i32,
    pub roots: Vec<String>,
    #[serde(default)]
    pub blocks: Vec<CarBlock>,
    pub size: Option<i64>,
}

/// File entry for directory creation.
#[derive(Debug, Clone)]
pub struct FileEntry {
    pub name: String,
    pub content: Option<Vec<u8>>,
    pub cid: Option<String>,
}

impl FileEntry {
    /// Create a new file entry.
    pub fn new(name: impl Into<String>) -> Self {
        Self {
            name: name.into(),
            content: None,
            cid: None,
        }
    }

    /// Set the content.
    pub fn content(mut self, content: Vec<u8>) -> Self {
        self.content = Some(content);
        self
    }

    /// Set the CID.
    pub fn cid(mut self, cid: impl Into<String>) -> Self {
        self.cid = Some(cid.into());
        self
    }
}

/// Directory entry.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DirectoryEntry {
    pub name: String,
    pub cid: String,
    pub size: Option<i64>,
    #[serde(rename = "type")]
    pub entry_type: EntryType,
}

/// Import CAR response.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ImportCarResponse {
    pub roots: Vec<String>,
    pub blocks_imported: i32,
}

/// Storage statistics.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct StorageStats {
    pub total_size: i64,
    pub pin_count: i32,
    pub bandwidth: Option<BandwidthStats>,
}

/// Bandwidth statistics.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BandwidthStats {
    pub upload: i64,
    pub download: i64,
}
