use std::collections::BTreeMap;

use emdb::{Emdb, Result};

fn tmp_path(name: &str) -> std::path::PathBuf {
    let mut p = std::env::temp_dir();
    let nanos = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .map_or(0_u128, |d| d.as_nanos());
    p.push(format!("emdb-{name}-{nanos}.emdb"));
    p
}

#[test]
fn compact_shrinks_file_and_preserves_state() -> Result<()> {
    let path = tmp_path("compact");

    let mut expected = BTreeMap::new();
    {
        let mut db = Emdb::open(&path)?;

        for i in 0_u32..1_000 {
            let key = format!("k{i}");
            let value = format!("value-{i:04}-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
            db.insert(key.as_bytes(), value.as_bytes())?;
            expected.insert(key, value.into_bytes());
        }

        for i in 0_u32..800 {
            let key = format!("k{i}");
            let _removed = db.remove(&key)?;
            expected.remove(&key);
        }

        for i in 800_u32..900 {
            let key = format!("k{i}");
            let value = format!("updated-{i:04}-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
            db.insert(key.as_bytes(), value.as_bytes())?;
            expected.insert(key, value.into_bytes());
        }

        db.flush()?;

        let before = std::fs::metadata(&path)?.len();
        db.compact()?;
        db.flush()?;
        let after = std::fs::metadata(&path)?.len();

        assert!(after < before);
    }

    let db = Emdb::open(&path)?;
    assert_eq!(db.len(), expected.len());
    for (k, v) in expected {
        assert_eq!(db.get(k.as_bytes())?, Some(v));
    }

    assert!(std::fs::remove_file(path).is_ok());
    Ok(())
}
