//! Serializable types for the archive domain.

use serde::{Deserialize, Serialize};

/// The format of the archive.
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ArchiveFormat {
    Tar,
    TarGz,
    Zip,
}

/// Metadata for a single file in the archive.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ArchiveEntry {
    pub path: String,
    pub oid: String,
    pub size: u64,
    pub mode: u32,
}

/// Summary of an archive.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ArchiveSummary {
    pub commit_oid: String,
    pub format: ArchiveFormat,
    pub total_entries: usize,
    pub total_size: u64,
}

/// Options for archive generation.
#[derive(Debug, Clone, Default)]
pub struct ArchiveOptions {
    /// Prefix path to prepend to all entries (e.g. "project-name/").
    pub prefix: Option<String>,
    /// Maximum directory depth to recurse into.
    pub max_depth: Option<usize>,
    /// Include only entries under this path prefix.
    pub path_filter: Option<String>,
}

impl ArchiveOptions {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn prefix(mut self, p: &str) -> Self {
        self.prefix = Some(p.to_string());
        self
    }

    pub fn max_depth(mut self, d: usize) -> Self {
        self.max_depth = Some(d);
        self
    }

    pub fn path_filter(mut self, p: &str) -> Self {
        self.path_filter = Some(p.to_string());
        self
    }

    /// Two Option sets with the same values produce the same key.
    pub(crate) fn cache_key(&self) -> String {
        let prefix = self.prefix.as_deref().unwrap_or("");
        let filter = self.path_filter.as_deref().unwrap_or("");
        let depth = self.max_depth.map_or("0".to_string(), |d| d.to_string());
        if prefix.is_empty() && filter.is_empty() && self.max_depth.is_none() {
            String::new()
        } else {
            use std::collections::hash_map::DefaultHasher;
            use std::hash::{Hash, Hasher};
            let mut h = DefaultHasher::new();
            (prefix, filter, depth).hash(&mut h);
            format!("-{:x}", h.finish())
        }
    }
}
