//! Comprehensive tests for SSTable component path building and discovery
//!
//! This test suite verifies that Index.db, Summary.db, and Statistics.db files
//! are correctly found and loaded, and that index-derived operations work properly.

use std::path::Path;
use std::sync::Arc;
use tempfile::TempDir;
use tokio::fs;

use cqlite_core::platform::Platform;
use cqlite_core::storage::sstable::{
    index_reader::IndexReader, statistics_reader::StatisticsReader, summary_reader::SummaryReader,
    SSTableReader,
};
use cqlite_core::Config;

/// Test that component file paths are built correctly from Data.db paths
#[tokio::test]
async fn test_component_path_building() {
    let temp_dir = TempDir::new().unwrap();
    let base_path = temp_dir.path();

    // Create mock SSTable files with Cassandra naming pattern
    let data_file = base_path.join("nb-1-big-Data.db");
    let index_file = base_path.join("nb-1-big-Index.db");
    let summary_file = base_path.join("nb-1-big-Summary.db");
    let statistics_file = base_path.join("nb-1-big-Statistics.db");

    // Create empty files to simulate presence
    fs::write(&data_file, b"mock_data").await.unwrap();
    fs::write(&index_file, b"mock_index").await.unwrap();
    fs::write(&summary_file, b"mock_summary").await.unwrap();
    fs::write(&statistics_file, b"mock_statistics")
        .await
        .unwrap();

    // Test path building logic
    let stem = data_file.file_stem().unwrap().to_str().unwrap();
    assert_eq!(stem, "nb-1-big-Data");

    // Remove "-Data" suffix to get base name
    let base_name = stem.strip_suffix("-Data").unwrap();
    assert_eq!(base_name, "nb-1-big");

    // Verify companion files exist
    assert!(index_file.exists());
    assert!(summary_file.exists());
    assert!(statistics_file.exists());
}

/// Test various SSTable file naming patterns
#[tokio::test]
async fn test_file_naming_patterns() {
    let temp_dir = TempDir::new().unwrap();
    let base_path = temp_dir.path();

    let test_patterns = vec![
        ("nb-1-big", "nb-1-big-Data.db"),
        ("mc-1-big", "mc-1-big-Data.db"),
        ("la-1-big", "la-1-big-Data.db"),
        ("users-123abc", "users-123abc-Data.db"),
        (
            "collections_table-456def",
            "collections_table-456def-Data.db",
        ),
    ];

    for (base_name, data_filename) in test_patterns {
        // Create data file
        let data_path = base_path.join(data_filename);
        fs::write(&data_path, b"mock_data").await.unwrap();

        // Create companion files
        let index_path = base_path.join(format!("{}-Index.db", base_name));
        let summary_path = base_path.join(format!("{}-Summary.db", base_name));
        let statistics_path = base_path.join(format!("{}-Statistics.db", base_name));

        fs::write(&index_path, b"mock_index").await.unwrap();
        fs::write(&summary_path, b"mock_summary").await.unwrap();
        fs::write(&statistics_path, b"mock_statistics")
            .await
            .unwrap();

        // Verify all files exist
        assert!(
            data_path.exists(),
            "Data file should exist: {}",
            data_filename
        );
        assert!(
            index_path.exists(),
            "Index file should exist for {}",
            base_name
        );
        assert!(
            summary_path.exists(),
            "Summary file should exist for {}",
            base_name
        );
        assert!(
            statistics_path.exists(),
            "Statistics file should exist for {}",
            base_name
        );

        // Clean up for next iteration
        fs::remove_file(&data_path).await.unwrap();
        fs::remove_file(&index_path).await.unwrap();
        fs::remove_file(&summary_path).await.unwrap();
        fs::remove_file(&statistics_path).await.unwrap();
    }
}

/// Test SSTableReader component loading methods
#[tokio::test]
async fn test_sstable_reader_component_loading() {
    let temp_dir = TempDir::new().unwrap();
    let base_path = temp_dir.path();

    // Create mock SSTable with valid headers (minimal)
    let data_file = base_path.join("nb-1-big-Data.db");
    let index_file = base_path.join("nb-1-big-Index.db");
    let summary_file = base_path.join("nb-1-big-Summary.db");
    let statistics_file = base_path.join("nb-1-big-Statistics.db");

    // Create minimal valid file headers
    create_mock_data_file(&data_file).await;
    create_mock_index_file(&index_file).await;
    create_mock_summary_file(&summary_file).await;
    create_mock_statistics_file(&statistics_file).await;

    let config = Config::default();
    let platform = Arc::new(Platform::new(&config).await.unwrap());

    // Test individual component loading
    test_index_reader_loading(&index_file, &platform).await;
    test_summary_reader_loading(&summary_file, &platform).await;
    test_statistics_reader_loading(&statistics_file, &platform).await;
}

async fn test_index_reader_loading(index_path: &Path, platform: &Arc<Platform>) {
    // IndexReader should be able to open the file (even if parsing fails)
    let result = IndexReader::open(index_path, platform.clone()).await;

    // We expect this to fail gracefully since we're using mock data
    // The important thing is that the file is found and opening is attempted
    match result {
        Ok(_) => {
            // If it succeeds with mock data, that's fine too
            println!("Index reader successfully opened mock file");
        }
        Err(e) => {
            // Expected - mock data likely doesn't have valid format
            println!("Index reader failed as expected with mock data: {}", e);
        }
    }
}

async fn test_summary_reader_loading(summary_path: &Path, platform: &Arc<Platform>) {
    let result = SummaryReader::open(summary_path, platform.clone()).await;

    match result {
        Ok(_) => {
            println!("Summary reader successfully opened mock file");
        }
        Err(e) => {
            println!("Summary reader failed as expected with mock data: {}", e);
        }
    }
}

async fn test_statistics_reader_loading(statistics_path: &Path, platform: &Arc<Platform>) {
    let result = StatisticsReader::open(statistics_path, platform.clone()).await;

    match result {
        Ok(_) => {
            println!("Statistics reader successfully opened mock file");
        }
        Err(e) => {
            println!("Statistics reader failed as expected with mock data: {}", e);
        }
    }
}

/// Test missing component files scenario
#[tokio::test]
async fn test_missing_component_files() {
    let temp_dir = TempDir::new().unwrap();
    let base_path = temp_dir.path();

    // Create only Data.db file, no companions
    let data_file = base_path.join("nb-1-big-Data.db");
    create_mock_data_file(&data_file).await;

    let config = Config::default();
    let platform = Arc::new(Platform::new(&config).await.unwrap());

    // Try to open non-existent companion files
    let index_path = base_path.join("nb-1-big-Index.db");
    let summary_path = base_path.join("nb-1-big-Summary.db");
    let statistics_path = base_path.join("nb-1-big-Statistics.db");

    // These should fail gracefully
    assert!(IndexReader::open(&index_path, platform.clone())
        .await
        .is_err());
    assert!(SummaryReader::open(&summary_path, platform.clone())
        .await
        .is_err());
    assert!(StatisticsReader::open(&statistics_path, platform.clone())
        .await
        .is_err());
}

/// Test malformed component files
#[tokio::test]
async fn test_malformed_component_files() {
    let temp_dir = TempDir::new().unwrap();
    let base_path = temp_dir.path();

    // Create files with invalid content
    let index_file = base_path.join("nb-1-big-Index.db");
    let summary_file = base_path.join("nb-1-big-Summary.db");
    let statistics_file = base_path.join("nb-1-big-Statistics.db");

    // Write invalid/corrupted data that will definitely cause parsing errors
    fs::write(&index_file, b"").await.unwrap(); // Empty file should fail
    fs::write(&summary_file, b"corrupted_summary")
        .await
        .unwrap();
    fs::write(&statistics_file, b"bad_statistics")
        .await
        .unwrap();

    let config = Config::default();
    let platform = Arc::new(Platform::new(&config).await.unwrap());

    // These should fail gracefully without crashing
    let index_result = IndexReader::open(&index_file, platform.clone()).await;
    let summary_result = SummaryReader::open(&summary_file, platform.clone()).await;
    let statistics_result = StatisticsReader::open(&statistics_file, platform.clone()).await;

    // All should return errors, not panic
    assert!(index_result.is_err());
    assert!(summary_result.is_err());
    assert!(statistics_result.is_err());
}

/// Test path building with edge cases
#[tokio::test]
async fn test_path_building_edge_cases() {
    let temp_dir = TempDir::new().unwrap();
    let base_path = temp_dir.path();

    // Test files with unusual but valid names
    let edge_cases = vec![
        "nb-1-big-Data.db",
        "mc-123-large-Data.db",
        "table_name-abc123def-Data.db",
        "users-46436710673711f0b2cf19d64e7cbecb-Data.db", // Real Cassandra pattern
    ];

    for data_filename in edge_cases {
        let data_path = base_path.join(data_filename);
        fs::write(&data_path, b"test").await.unwrap();

        // Extract base name (remove -Data.db suffix)
        let file_stem = data_path.file_stem().unwrap().to_str().unwrap();
        if let Some(base_name) = file_stem.strip_suffix("-Data") {
            // Build companion paths
            let index_path = base_path.join(format!("{}-Index.db", base_name));
            let summary_path = base_path.join(format!("{}-Summary.db", base_name));
            let statistics_path = base_path.join(format!("{}-Statistics.db", base_name));

            // Verify path construction is correct
            assert!(index_path.to_string_lossy().ends_with("-Index.db"));
            assert!(summary_path.to_string_lossy().ends_with("-Summary.db"));
            assert!(statistics_path
                .to_string_lossy()
                .ends_with("-Statistics.db"));

            println!("✓ Path building works for: {}", data_filename);
        }

        fs::remove_file(&data_path).await.unwrap();
    }
}

// Helper functions to create minimal mock files

async fn create_mock_data_file(path: &Path) {
    // Create a minimal mock Data.db file with basic SSTable structure
    let mock_header = vec![
        0x01, 0x00, 0x00, 0x00, // Version
        0x00, 0x00, 0x00, 0x00, // Minimal header
    ];
    fs::write(path, mock_header).await.unwrap();
}

async fn create_mock_index_file(path: &Path) {
    // Create a minimal mock Index.db file
    let mock_index = vec![
        0x00, 0x00, 0x00, 0x01, // Entry count
        0x00, 0x00, 0x00, 0x00, // Minimal index data
    ];
    fs::write(path, mock_index).await.unwrap();
}

async fn create_mock_summary_file(path: &Path) {
    // Create a minimal mock Summary.db file
    let mock_summary = vec![
        0x00, 0x00, 0x00, 0x01, // Entry count
        0x00, 0x00, 0x00, 0x00, // Minimal summary data
    ];
    fs::write(path, mock_summary).await.unwrap();
}

async fn create_mock_statistics_file(path: &Path) {
    // Create a minimal mock Statistics.db file
    let mock_stats = vec![
        0x00, 0x00, 0x00, 0x10, // Length prefix
        0x74, 0x65, 0x73, 0x74, // "test" entry
    ];
    fs::write(path, mock_stats).await.unwrap();
}

#[cfg(test)]
mod component_integration_tests {
    use super::*;

    /// Integration test for full SSTable workflow with all components
    #[tokio::test]
    async fn test_full_sstable_workflow_with_components() {
        let temp_dir = TempDir::new().unwrap();
        let base_path = temp_dir.path();

        // Create a complete set of SSTable files
        let data_file = base_path.join("nb-1-big-Data.db");
        let index_file = base_path.join("nb-1-big-Index.db");
        let summary_file = base_path.join("nb-1-big-Summary.db");
        let statistics_file = base_path.join("nb-1-big-Statistics.db");

        create_mock_data_file(&data_file).await;
        create_mock_index_file(&index_file).await;
        create_mock_summary_file(&summary_file).await;
        create_mock_statistics_file(&statistics_file).await;

        let config = Config::default();
        let platform = Arc::new(Platform::new(&config).await.unwrap());

        // Try to create SSTableReader (may fail with mock data, but should attempt loading)
        let reader_result = SSTableReader::open(&data_file, &config, platform).await;

        match reader_result {
            Ok(reader) => {
                println!("✓ SSTableReader successfully created with all components");

                // Test that component readers are loaded (even if they contain mock data)
                // Note: These operations may fail due to mock data, but that's expected
                let _ = reader.get_timestamp_range().await;
                // Note: get_token_coverage is deprecated (Issue #218) - tokens not stored in Summary.db
            }
            Err(e) => {
                println!(
                    "✓ SSTableReader creation failed as expected with mock data: {}",
                    e
                );
                // This is expected behavior with mock data
            }
        }
    }

    /// Test that index-derived operations are no longer dead code
    #[tokio::test]
    async fn test_index_derived_operations_not_dead_code() {
        // This test verifies that index-related functionality is actually used
        let temp_dir = TempDir::new().unwrap();
        let base_path = temp_dir.path();

        let data_file = base_path.join("nb-1-big-Data.db");
        create_mock_data_file(&data_file).await;

        let config = Config::default();
        let platform = Arc::new(Platform::new(&config).await.unwrap());

        if let Ok(reader) = SSTableReader::open(&data_file, &config, platform).await {
            // Test partition lookup (uses Index.db)
            let test_key = b"test_partition_key";
            let _lookup_result = reader.lookup_partition_with_index(test_key).await;

            // Test schema-driven lookup (uses Index.db with schema)
            // Schema context lookup requires a ParsingContext - skip for now
            // let _schema_lookup_result = reader.lookup_partition_with_schema_context(test_key, context).await;

            // Test partition iteration (uses Summary.db)
            // Note: iterate_token_range is deprecated (Issue #218) - use iterate_all_partitions
            let _partitions = reader.iterate_all_partitions().await;

            // These methods should be reachable and not marked as dead code
            println!("✓ Index-derived operations are accessible and not dead code");
        }
    }
}
