// tests/test_compression_all_backends.rs
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
// SPDX-FileCopyrightText: 2025 Russ Fellows <russ.fellows@gmail.com>

use anyhow::Result;
use tempfile::tempdir;
use std::path::PathBuf;

use s3dlio::object_store::{ObjectWriter, CompressionConfig, store_for_uri};
use s3dlio::file_store::FileSystemWriter;
use s3dlio::file_store_direct::{DirectIOWriter, FileSystemConfig};

// Test data that compresses well
fn get_test_data() -> Vec<u8> {
    b"Hello, compression test! ".repeat(100)
}

#[tokio::test]
async fn test_filesystem_compression() -> Result<()> {
    let temp_dir = tempdir()?;
    let file_path = temp_dir.path().join("test_fs_compression.dat");
    let test_data = get_test_data();
    
    // Test with compression
    let mut writer = FileSystemWriter::new_with_compression(
        file_path.clone(),
        CompressionConfig::zstd_level(5)
    ).await?;
    
    writer.write_chunk(&test_data).await?;
    let bytes_written = writer.bytes_written();
    
    // Finalize first, then check compression ratio
    let boxed_writer = Box::new(writer);
    boxed_writer.finalize().await?;
    
    // Verify compression worked
    assert_eq!(bytes_written, test_data.len() as u64);
    
    // Verify file has compression extension
    let compressed_path = format!("{}.zst", file_path.display());
    assert!(PathBuf::from(&compressed_path).exists());
    
    // Check file size to verify compression
    let original_size = test_data.len();
    let compressed_size = std::fs::metadata(&compressed_path)?.len();
    let ratio = compressed_size as f64 / original_size as f64;
    
    assert!(ratio < 1.0, "Data should be compressed (ratio: {:.2})", ratio);
    println!("✅ FileSystem compression test passed (ratio: {:.2})", ratio);
    Ok(())
}

#[tokio::test]
async fn test_directio_compression() -> Result<()> {
    let temp_dir = tempdir()?;
    let file_path = temp_dir.path().join("test_directio_compression.dat");
    let test_data = get_test_data();
    
    let config = FileSystemConfig {
        direct_io: false, // Use regular I/O for testing
        sync_writes: false,
        alignment: 4096,
        min_io_size: 4096,
        enable_range_engine: false,
        range_engine: Default::default(),
        buffer_pool: None,
    };
    
    // Test with compression
    let mut writer = DirectIOWriter::new_with_compression(
        file_path.clone(),
        config,
        CompressionConfig::zstd_level(3)
    ).await?;
    
    writer.write_chunk(&test_data).await?;
    let bytes_written = writer.bytes_written();
    let compression_ratio = writer.compression_ratio();
    
    Box::new(writer).finalize().await?;
    
    // Verify compression worked
    assert_eq!(bytes_written, test_data.len() as u64);
    assert!(compression_ratio < 1.0, "Data should be compressed");
    
    println!("✅ DirectIO compression test passed (ratio: {:.2})", compression_ratio);
    Ok(())
}

#[tokio::test]
async fn test_s3_compression_via_store() -> Result<()> {
    let s3_uri = format!("s3://test-bucket/test_s3_compression.dat");
    
    // Note: This test will fail if not connected to S3, but tests the interface
    let store = store_for_uri(&s3_uri);
    assert!(store.is_ok(), "S3 store creation should succeed");
    
    // Test compression is available in the interface
    println!("✅ S3 compression interface test passed");
    Ok(())
}

#[cfg(feature = "backend-azure")]
#[tokio::test]
async fn test_azure_compression_via_store() -> Result<()> {
    let azure_uri = "az://testaccount/testcontainer/test_azure_compression.dat";
    
    // Note: This test will fail if not connected to Azure, but tests the interface
    let store = store_for_uri(azure_uri);
    assert!(store.is_ok(), "Azure store creation should succeed");
    
    // Test compression is available in the interface
    println!("✅ Azure compression interface test passed");
    Ok(())
}

#[tokio::test]
async fn test_compression_levels_across_backends() -> Result<()> {
    let temp_dir = tempdir()?;
    let test_data = get_test_data();
    
    for level in [1, 3, 5, 10, 15, 22] {
        // Test FileSystem
        let file_path = temp_dir.path().join(format!("test_level_{}.dat", level));
        let mut writer = FileSystemWriter::new_with_compression(
            file_path.clone(),
            CompressionConfig::zstd_level(level)
        ).await?;
        
        writer.write_chunk(&test_data).await?;
        Box::new(writer).finalize().await?;
        
        // Check file size to verify compression
        let compressed_path = format!("{}.zst", file_path.display());
        let original_size = test_data.len();
        let compressed_size = std::fs::metadata(&compressed_path)?.len();
        let compression_ratio = compressed_size as f64 / original_size as f64;
        
        assert!(compression_ratio < 1.0, "Level {} should compress", level);
        println!("✅ Compression level {} test passed (ratio: {:.2})", level, compression_ratio);
    }
    
    Ok(())
}

#[tokio::test]
async fn test_compression_disabled_across_backends() -> Result<()> {
    let temp_dir = tempdir()?;
    let test_data = get_test_data();
    
    // Test FileSystem without compression
    let file_path = temp_dir.path().join("test_no_compression.dat");
    let mut writer = FileSystemWriter::new_with_compression(
        file_path.clone(),
        CompressionConfig::None
    ).await?;
    
    writer.write_chunk(&test_data).await?;
    let compression_ratio = writer.compression_ratio();
    Box::new(writer).finalize().await?;
    
    // No compression means ratio should be 1.0
    assert_eq!(compression_ratio, 1.0);
    
    // File should not have compression extension
    assert!(file_path.exists());
    
    println!("✅ No compression test passed");
    Ok(())
}

#[tokio::test]
async fn test_compression_with_integrity() -> Result<()> {
    let temp_dir = tempdir()?;
    let file_path = temp_dir.path().join("test_compression_integrity.dat");
    let test_data = get_test_data();
    
    // Test with compression and verify checksum
    let mut writer = FileSystemWriter::new_with_compression(
        file_path.clone(),
        CompressionConfig::zstd_level(7)
    ).await?;
    
    writer.write_chunk(&test_data).await?;
    let checksum = writer.checksum();
    
    Box::new(writer).finalize().await?;
    
    // Check file size to verify compression
    let compressed_path = format!("{}.zst", file_path.display());
    let original_size = test_data.len();
    let compressed_size = std::fs::metadata(&compressed_path)?.len();
    let compression_ratio = compressed_size as f64 / original_size as f64;
    
    // Verify both compression and checksum work
    assert!(compression_ratio < 1.0, "Data should be compressed");
    assert!(checksum.is_some(), "Checksum should be available");
    assert!(checksum.unwrap().starts_with("crc32c:"), "Should be CRC32C checksum");
    
    println!("✅ Compression with integrity test passed");
    Ok(())
}
