use crate::utils::{db_put_simple, open_database, tmpdir};
use leveldb::database::Database;
use leveldb::database::compaction::Compaction;
use leveldb::database::slice::Slice;
use leveldb::database::statistics::{KeyRange, Statistics};

#[test]
fn test_get_num_files_at_level() {
    let tmp = tmpdir("statistics");
    let database = open_database(tmp.path(), true);

    // Write some data to ensure level 0 has files
    db_put_simple(&database, b"1", b"1");

    // Test level 0 (should exist and have files after writing data)
    let result = database.get_num_files_at_level(0);
    assert!(result.is_ok());
    assert!(
        result.unwrap().is_some(),
        "Level 0 should have files after writing data"
    );

    // Test a high level (likely doesn't exist)
    let result = database.get_num_files_at_level(7);
    assert!(result.is_ok());
    assert!(
        result.unwrap().is_none(),
        "Level 7 should have no file at this moment"
    );
}

#[test]
fn test_get_stats() {
    let tmp = tmpdir("statistics");
    let database = open_database(tmp.path(), true);

    let result = database.get_stats();

    assert!(result.is_ok());
    // Stats property should exist
    assert!(result.unwrap().is_some());
}

#[test]
fn test_get_sstables() {
    let tmp = tmpdir("statistics");
    let database = open_database(tmp.path(), true);

    let result = database.get_property_of_sstables();

    assert!(result.is_ok());
    // SSTables property may or may not exist depending on database state
    let property = result.unwrap();
    // Just ensure we don't get an error
    assert!(property.is_some() || property.is_none());
}

#[test]
fn test_get_approximate_memory_usage() {
    let tmp = tmpdir("statistics");
    let database = open_database(tmp.path(), true);

    let result = database.get_approximate_memory_usage();

    assert!(result.is_ok());
    // Memory usage property should exist
    let property = result.unwrap();
    assert!(property.is_some());
}

fn key_range_of(start: u16, end: u16) -> KeyRange {
    let s = &start.to_be_bytes()[..];
    let e = &end.to_be_bytes()[..];
    KeyRange::new(Slice::from(s), Slice::from(e))
}

fn prepare(database: &Database) {
    // Write substantial test data to ensure it gets persisted
    for i in 0..1000_u16 {
        let key = i.to_be_bytes();
        db_put_simple(
            database,
            &key,
            format!("value_{}_with_some_padding_to_make_it_larger", i).as_bytes(),
        );
    }
    // Force compaction to ensure data is in SST files
    let r = key_range_of(0, 999);
    database.compact(r.start(), r.end());
}

#[test]
fn test_get_approximate_sizes_single_range() {
    let tmp = tmpdir("statistics");
    let database = open_database(tmp.path(), true);
    prepare(&database);

    // Test single range - use a larger range like in the demo test
    let range = key_range_of(100, 500);
    let sizes = database.get_approximate_sizes(vec![range]);

    assert_eq!(sizes.len(), 1);
    assert!(sizes[0] > 0, "Expected size > 0, got {}", sizes[0]);
}

#[test]
fn test_get_approximate_sizes_multiple_ranges() {
    let tmp = tmpdir("statistics");
    let database = open_database(tmp.path(), true);
    prepare(&database);

    // Test multiple ranges - use larger ranges like in the demo test
    let ranges = vec![
        key_range_of(0, 250),
        key_range_of(250, 500),
        key_range_of(500, 750),
        key_range_of(750, 1000),
    ];
    let sizes = database.get_approximate_sizes(ranges);

    assert_eq!(sizes.len(), 4);
    for (i, size) in sizes.iter().enumerate() {
        assert!(*size > 0, "Range {} expected size > 0, got {}", i, size);
    }
}

#[test]
fn test_get_approximate_size_for_range() {
    let tmp = tmpdir("statistics");
    let database = open_database(tmp.path(), true);
    prepare(&database);

    // Test case 1: Range with data (keys 250-750 exist in our prepared data)
    let r = key_range_of(250, 750);
    let size_with_data = database.get_approximate_size_for_range(r.start(), r.end());
    assert!(
        size_with_data > 0,
        "Expected size to be greater than 0, got {}",
        size_with_data
    );

    // Test case 2: Range without data (keys 2000-3000 don't exist in our prepared data)
    let r = key_range_of(2000, 3000);
    let size_without_data = database.get_approximate_size_for_range(r.start(), r.end());
    assert_eq!(
        size_without_data, 0,
        "Expected size to be 0, got {}",
        size_without_data
    );
}

#[test]
fn test_get_approximate_sizes_empty_ranges() {
    let tmp = tmpdir("statistics");
    let database = open_database(tmp.path(), true);

    // Test with empty ranges array
    let sizes = database.get_approximate_sizes(vec![]);
    assert_eq!(sizes.len(), 0);

    // Test with range that has no data
    let range = key_range_of(1000, 2000);
    let sizes = database.get_approximate_sizes(vec![range]);

    assert_eq!(sizes.len(), 1);
    assert_eq!(sizes[0], 0);
}
