// Core Formats Integration Tests

use std::fs;
use std::io::Read;
use tempfile::TempDir;

use crate::archive::*;
use crate::archive::tests::test_helpers::*;

#[test]
fn test_zip_processor_full_workflow() {
    let temp_dir = TempDir::new().unwrap();
    let zip_path = create_test_zip(&temp_dir);
    
    // Initialize processor
    let mut processor = processors::core::ZipProcessor::new();
    
    // Test open
    let metadata = processor.open(&zip_path).unwrap();
    assert_eq!(metadata.format, ArchiveFormat::Zip);
    assert_eq!(metadata.total_files, 3);
    
    // Test list_entries
    let entries = processor.list_entries().unwrap();
    assert_eq!(entries.len(), 3);
    
    // Verify entry names
    let names: Vec<&str> = entries.iter()
        .map(|e| e.path.to_str().unwrap())
        .collect();
    assert!(names.contains(&"file1.txt"));
    assert!(names.contains(&"file2.txt"));
    assert!(names.contains(&"subdir/file3.txt"));
    
    // Test extract_all
    let extract_dir = temp_dir.path().join("extracted");
    fs::create_dir_all(&extract_dir).unwrap();
    
    let result = processor.extract_all(&extract_dir).unwrap();
    assert_eq!(result.success_files, 3);
    assert_eq!(result.failed_files.len(), 0);
    
    // Verify extracted files
    assert!(extract_dir.join("file1.txt").exists());
    assert!(extract_dir.join("file2.txt").exists());
    assert!(extract_dir.join("subdir/file3.txt").exists());
    
    // Verify content
    let content1 = fs::read_to_string(extract_dir.join("file1.txt")).unwrap();
    assert_eq!(content1, "content of file 1");
}

#[test]
fn test_tar_processor_full_workflow() {
    let temp_dir = TempDir::new().unwrap();
    let tar_path = create_test_tar(&temp_dir);
    
    let mut processor = processors::core::TarProcessor::new();
    
    // Test open
    let metadata = processor.open(&tar_path).unwrap();
    assert_eq!(metadata.format, ArchiveFormat::Tar);
    
    // Test list_entries
    let entries = processor.list_entries().unwrap();
    assert!(entries.len() >= 3);  // TAR may include directory entries
    
    // Test extract_all
    let extract_dir = temp_dir.path().join("extracted_tar");
    fs::create_dir_all(&extract_dir).unwrap();
    
    let result = processor.extract_all(&extract_dir).unwrap();
    assert!(result.success_files >= 3);
    
    // Verify extracted files exist
    assert!(extract_dir.join("file1.txt").exists());
    assert!(extract_dir.join("file2.txt").exists());
}

#[test]
fn test_gzip_processor_full_workflow() {
    let temp_dir = TempDir::new().unwrap();
    let gz_path = create_test_gzip(&temp_dir);
    
    let mut processor = processors::core::GzipProcessor::new();
    
    // Test open
    let metadata = processor.open(&gz_path).unwrap();
    assert_eq!(metadata.format, ArchiveFormat::Gzip);
    assert_eq!(metadata.total_files, 1);  // GZIP is single file
    
    // Test extract_all
    let extract_dir = temp_dir.path().join("extracted_gz");
    fs::create_dir_all(&extract_dir).unwrap();
    
    let result = processor.extract_all(&extract_dir).unwrap();
    assert_eq!(result.success_files, 1);
    
    // Verify extracted file (should strip .gz extension)
    let extracted_file = extract_dir.join("test.txt");
    assert!(extracted_file.exists());
    
    // Verify content
    let content = fs::read_to_string(&extracted_file).unwrap();
    assert_eq!(content, "test gzip content for validation");
}

#[test]
fn test_tar_gz_processor_workflow() {
    let temp_dir = TempDir::new().unwrap();
    let tar_gz_path = create_test_tar_gz(&temp_dir);
    
    let mut processor = processors::core::TarGzipProcessor::new();
    
    // Test open
    let metadata = processor.open(&tar_gz_path).unwrap();
    assert_eq!(metadata.format, ArchiveFormat::TarGzip);
    
    // Test extract_all
    let extract_dir = temp_dir.path().join("extracted_tar_gz");
    fs::create_dir_all(&extract_dir).unwrap();
    
    let result = processor.extract_all(&extract_dir).unwrap();
    assert!(result.success_files >= 2);
    
    // Verify extracted TAR files
    assert!(extract_dir.join("file1.txt").exists());
    assert!(extract_dir.join("file2.txt").exists());
}

#[test]
fn test_format_detection_auto() {
    let temp_dir = TempDir::new().unwrap();
    
    // Test ZIP detection
    let zip_path = create_test_zip(&temp_dir);
    let detector = FormatDetector::new();
    let format = detector.detect(&zip_path).unwrap();
    assert_eq!(format, ArchiveFormat::Zip);
    
    // Test TAR detection
    let tar_path = create_test_tar(&temp_dir);
    let format = detector.detect(&tar_path).unwrap();
    assert_eq!(format, ArchiveFormat::Tar);
    
    // Test GZIP detection
    let gz_path = create_test_gzip(&temp_dir);
    let format = detector.detect(&gz_path).unwrap();
    assert_eq!(format, ArchiveFormat::Gzip);
}

#[test]
fn test_processor_registry_core_formats() {
    let config = ArchiveConfig::default();
    let mut registry = ProcessorRegistry::new(config);
    registry.initialize().unwrap();
    
    let formats = registry.enabled_formats();
    
    // Should have 9 core formats
    assert!(formats.len() >= 4);  // At least the ones we implemented
    
    // Verify format support
    assert!(formats.contains(&ArchiveFormat::Zip));
    assert!(formats.contains(&ArchiveFormat::Tar));
    assert!(formats.contains(&ArchiveFormat::Gzip));
    assert!(formats.contains(&ArchiveFormat::TarGzip));
}

#[test]
fn test_zip_slip_protection() {
    let temp_dir = TempDir::new().unwrap();
    let zip_bomb_data = create_zip_slip_test();
    
    // Write malicious ZIP to file
    let evil_zip_path = temp_dir.path().join("evil.zip");
    fs::write(&evil_zip_path, &zip_bomb_data).unwrap();
    
    let mut processor = processors::core::ZipProcessor::new();
    processor.open(&evil_zip_path).unwrap();
    
    // Attempt extraction should fail due to Zip Slip protection
    let extract_dir = temp_dir.path().join("should_fail");
    fs::create_dir_all(&extract_dir).unwrap();
    
    let result = processor.extract_all(&extract_dir);
    
    // Should either fail or have empty extracted files
    // (validate_extraction_path prevents malicious paths)
    if result.is_ok() {
        let result = result.unwrap();
        // If extraction succeeded, malicious file should not exist
        assert!(!extract_dir.join("etc/passwd").exists());
    }
}

#[test]
fn test_zip_bomb_detection() {
    // Test decompression ratio check
    let result = check_decompression_ratio(42_000, 5_000_000_000, 1000);
    assert!(result.is_err());  // Should detect as Zip Bomb
    
    // Test normal ratio
    let result = check_decompression_ratio(1000, 5000, 1000);
    assert!(result.is_ok());  // Normal ratio should pass
}

#[test]
fn test_metadata_compression_ratio() {
    let metadata = ArchiveMetadata {
        format: ArchiveFormat::Zip,
        total_files: 10,
        total_size: 5000,
        compressed_size: 1000,
        compression_ratio: 0.0,
        is_encrypted: false,
        is_multi_volume: false,
        created_time: None,
        modified_time: None,
    };
    
    assert_eq!(metadata.actual_ratio(), 5.0);  // 5000/1000 = 5.0
    assert!(!metadata.check_zip_bomb(10));  // ratio 5.0 < 10, not a bomb
    assert!(metadata.check_zip_bomb(4));  // ratio 5.0 > 4, detected as bomb
}

#[test]
fn test_config_validation() {
    let config = ArchiveConfig {
        max_decompression_ratio: 5,  // Too low
        ..Default::default()
    };
    
    assert!(config.validate().is_err());
    
    let valid_config = ArchiveConfig::default();
    assert!(valid_config.validate().is_ok());
}