// Metadata Module - Archive Entry Metadata Management

use std::path::PathBuf;
use std::time::SystemTime;
use serde::{Deserialize, Serialize};

use crate::archive::processor::ArchiveFormat;

/// Archive Metadata - Full Information About Compressed File
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ArchiveMetadata {
    pub format: ArchiveFormat,
    pub total_files: u64,
    pub total_size: u64,
    pub compressed_size: u64,
    pub compression_ratio: f64,
    pub is_encrypted: bool,
    pub is_multi_volume: bool,
    pub created_time: Option<SystemTime>,
    pub modified_time: Option<SystemTime>,
}

impl ArchiveMetadata {
    /// Calculate actual compression ratio
    pub fn actual_ratio(&self) -> f64 {
        if self.compressed_size == 0 {
            0.0
        } else {
            self.total_size as f64 / self.compressed_size as f64
        }
    }
    
    /// Check if compression ratio exceeds limit (Zip Bomb detection)
    pub fn check_zip_bomb(&self, max_ratio: u64) -> bool {
        self.actual_ratio() > max_ratio as f64
    }
}

/// Archive Entry - Single File Entry in Archive
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ArchiveEntry {
    pub path: PathBuf,
    pub size: u64,
    pub compressed_size: u64,
    pub is_dir: bool,
    pub is_file: bool,
    pub is_encrypted: bool,
    pub modified: SystemTime,
    pub permissions: Option<u32>,
    pub checksum: Option<String>,
}

impl ArchiveEntry {
    /// Create directory entry
    pub fn directory(path: PathBuf) -> Self {
        Self {
            path,
            size: 0,
            compressed_size: 0,
            is_dir: true,
            is_file: false,
            is_encrypted: false,
            modified: SystemTime::UNIX_EPOCH,
            permissions: Some(0o755),
            checksum: None,
        }
    }
    
    /// Create file entry
    pub fn file(path: PathBuf, size: u64, compressed_size: u64) -> Self {
        Self {
            path,
            size,
            compressed_size,
            is_dir: false,
            is_file: true,
            is_encrypted: false,
            modified: SystemTime::UNIX_EPOCH,
            permissions: Some(0o644),
            checksum: None,
        }
    }
}

/// Extract Result - Summary of Extraction Operation
#[derive(Debug, Clone)]
pub struct ExtractResult {
    pub total_files: u64,
    pub total_bytes: u64,
    pub success_files: u64,
    pub failed_files: Vec<PathBuf>,
    pub skipped_files: Vec<PathBuf>,
    pub warnings: Vec<String>,
}

impl ExtractResult {
    pub fn new() -> Self {
        Self {
            total_files: 0,
            total_bytes: 0,
            success_files: 0,
            failed_files: Vec::new(),
            skipped_files: Vec::new(),
            warnings: Vec::new(),
        }
    }
    
    pub fn success_rate(&self) -> f64 {
        if self.total_files == 0 {
            100.0
        } else {
            let success_count = self.success_files;
            (success_count as f64 / self.total_files as f64) * 100.0
        }
    }
    
    pub fn has_failures(&self) -> bool {
        !self.failed_files.is_empty()
    }
    
    pub fn has_warnings(&self) -> bool {
        !self.warnings.is_empty()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_archive_metadata() {
        let metadata = ArchiveMetadata {
            format: ArchiveFormat::Zip,
            total_files: 10,
            total_size: 1000,
            compressed_size: 500,
            compression_ratio: 0.0,
            is_encrypted: false,
            is_multi_volume: false,
            created_time: None,
            modified_time: None,
        };
        
        assert_eq!(metadata.actual_ratio(), 2.0);
        assert!(!metadata.check_zip_bomb(1000));
        assert!(metadata.check_zip_bomb(1));  // Should detect as bomb
    }
    
    #[test]
    fn test_archive_entry() {
        let dir_entry = ArchiveEntry::directory(PathBuf::from("test_dir"));
        assert!(dir_entry.is_dir);
        assert!(!dir_entry.is_file);
        
        let file_entry = ArchiveEntry::file(PathBuf::from("test.txt"), 100, 50);
        assert!(!file_entry.is_dir);
        assert!(file_entry.is_file);
        assert_eq!(file_entry.size, 100);
    }
    
    #[test]
    fn test_extract_result() {
        let result = ExtractResult::new();
        assert_eq!(result.success_rate(), 100.0);
        
        let result_with_failure = ExtractResult {
            total_files: 10,
            success_files: 8,
            ..Default::default()
        };
        
        assert_eq!(result_with_failure.success_rate(), 80.0);
        assert!(result_with_failure.has_failures());
    }
}