#![cfg(feature = "armour")]

use armdb::armour::Db;
use serde::{Deserialize, Serialize};
use tempfile::tempdir;

#[test]
fn test_version_default_is_zero() {
    let dir = tempdir().unwrap();
    let db = Db::open_test(dir.path()).unwrap();
    assert_eq!(db.version(), 0);
}

#[test]
fn test_set_version_returns_new_value() {
    let dir = tempdir().unwrap();
    let db = Db::open_test(dir.path()).unwrap();
    let v = db
        .set_version(|prev| {
            assert_eq!(prev, 0);
            5
        })
        .unwrap();
    assert_eq!(v, 5);
    assert_eq!(db.version(), 5);
}

#[test]
fn test_version_persists_across_reopens() {
    let dir = tempdir().unwrap();
    {
        let db = Db::open_test(dir.path()).unwrap();
        db.set_version(|_| 3).unwrap();
        db.close().unwrap();
    }
    {
        let db = Db::open_test(dir.path()).unwrap();
        assert_eq!(db.version(), 3);
    }
}

#[test]
fn test_set_version_mutation_callback() {
    let dir = tempdir().unwrap();
    let db = Db::open_test(dir.path()).unwrap();
    db.set_version(|_| 1).unwrap();
    let v = db.set_version(|prev| prev + 1).unwrap();
    assert_eq!(v, 2);
}

// ---------------------------------------------------------------------------
// Database metadata tests
// ---------------------------------------------------------------------------

#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct AppMeta {
    app_name: String,
    build: u32,
}

#[test]
fn test_metadata_none_by_default() {
    let dir = tempdir().unwrap();
    let db = Db::open_test(dir.path()).unwrap();
    assert_eq!(db.metadata::<AppMeta>(), None);
}

#[test]
fn test_set_metadata_returns_new_value() {
    let dir = tempdir().unwrap();
    let db = Db::open_test(dir.path()).unwrap();
    let meta = db
        .set_metadata(|prev: Option<AppMeta>| {
            assert!(prev.is_none());
            AppMeta {
                app_name: "test".into(),
                build: 1,
            }
        })
        .unwrap();
    assert_eq!(meta.app_name, "test");
    assert_eq!(meta.build, 1);
}

#[test]
fn test_metadata_roundtrip() {
    let dir = tempdir().unwrap();
    let db = Db::open_test(dir.path()).unwrap();
    db.set_metadata(|_: Option<AppMeta>| AppMeta {
        app_name: "myapp".into(),
        build: 42,
    })
    .unwrap();
    let read = db.metadata::<AppMeta>().unwrap();
    assert_eq!(read.app_name, "myapp");
    assert_eq!(read.build, 42);
}

#[test]
fn test_metadata_mutation() {
    let dir = tempdir().unwrap();
    let db = Db::open_test(dir.path()).unwrap();
    db.set_metadata(|_: Option<AppMeta>| AppMeta {
        app_name: "app".into(),
        build: 1,
    })
    .unwrap();
    let meta = db
        .set_metadata(|prev: Option<AppMeta>| {
            let mut m = prev.unwrap();
            m.build += 1;
            m
        })
        .unwrap();
    assert_eq!(meta.build, 2);
}

#[test]
fn test_metadata_persists_across_reopens() {
    let dir = tempdir().unwrap();
    {
        let db = Db::open_test(dir.path()).unwrap();
        db.set_metadata(|_: Option<AppMeta>| AppMeta {
            app_name: "persist".into(),
            build: 7,
        })
        .unwrap();
        db.close().unwrap();
    }
    {
        let db = Db::open_test(dir.path()).unwrap();
        let meta = db.metadata::<AppMeta>().unwrap();
        assert_eq!(meta.app_name, "persist");
        assert_eq!(meta.build, 7);
    }
}

// ---------------------------------------------------------------------------
// Collection metadata tests
// ---------------------------------------------------------------------------

#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct ColMeta {
    description: String,
    indexed: bool,
}

#[test]
fn test_collection_metadata_none_by_default() {
    let dir = tempdir().unwrap();
    let db = Db::open_test(dir.path()).unwrap();
    assert_eq!(db.collection_metadata::<ColMeta>("users"), None);
}

#[test]
fn test_set_collection_metadata_returns_new_value() {
    let dir = tempdir().unwrap();
    let db = Db::open_test(dir.path()).unwrap();
    let meta = db
        .set_collection_metadata("users", |prev: Option<ColMeta>| {
            assert!(prev.is_none());
            ColMeta {
                description: "User accounts".into(),
                indexed: true,
            }
        })
        .unwrap();
    assert_eq!(meta.description, "User accounts");
    assert!(meta.indexed);
}

#[test]
fn test_collection_metadata_roundtrip() {
    let dir = tempdir().unwrap();
    let db = Db::open_test(dir.path()).unwrap();
    db.set_collection_metadata("orders", |_: Option<ColMeta>| ColMeta {
        description: "Orders".into(),
        indexed: false,
    })
    .unwrap();
    let read = db.collection_metadata::<ColMeta>("orders").unwrap();
    assert_eq!(read.description, "Orders");
    assert!(!read.indexed);
}

#[test]
fn test_collection_metadata_independent_per_collection() {
    let dir = tempdir().unwrap();
    let db = Db::open_test(dir.path()).unwrap();
    db.set_collection_metadata("a", |_: Option<ColMeta>| ColMeta {
        description: "AAA".into(),
        indexed: true,
    })
    .unwrap();
    db.set_collection_metadata("b", |_: Option<ColMeta>| ColMeta {
        description: "BBB".into(),
        indexed: false,
    })
    .unwrap();
    let a = db.collection_metadata::<ColMeta>("a").unwrap();
    let b = db.collection_metadata::<ColMeta>("b").unwrap();
    assert_eq!(a.description, "AAA");
    assert_eq!(b.description, "BBB");
    assert!(a.indexed);
    assert!(!b.indexed);
}

#[test]
fn test_collection_metadata_mutation() {
    let dir = tempdir().unwrap();
    let db = Db::open_test(dir.path()).unwrap();
    db.set_collection_metadata("x", |_: Option<ColMeta>| ColMeta {
        description: "initial".into(),
        indexed: false,
    })
    .unwrap();
    let meta = db
        .set_collection_metadata("x", |prev: Option<ColMeta>| {
            let mut m = prev.unwrap();
            m.indexed = true;
            m
        })
        .unwrap();
    assert!(meta.indexed);
    assert_eq!(meta.description, "initial");
}

#[test]
fn test_collection_metadata_persists_across_reopens() {
    let dir = tempdir().unwrap();
    {
        let db = Db::open_test(dir.path()).unwrap();
        db.set_collection_metadata("col1", |_: Option<ColMeta>| ColMeta {
            description: "persisted".into(),
            indexed: true,
        })
        .unwrap();
        db.close().unwrap();
    }
    {
        let db = Db::open_test(dir.path()).unwrap();
        let meta = db.collection_metadata::<ColMeta>("col1").unwrap();
        assert_eq!(meta.description, "persisted");
        assert!(meta.indexed);
    }
}
