use mad_hatter::file_store;
use mad_hatter::{ConceptMapToken, SuppressibleToken, Critical};
use std::path::PathBuf;

#[derive(Copy, Clone, Debug)]
struct TestToken;
impl ConceptMapToken for TestToken {
    fn operation_name(&self) -> &'static str { "test" }
}
impl SuppressibleToken for TestToken {}
fn uc<T>(c: Critical<T>) -> T {
    c.suppress(TestToken, "test").unwrap()
}

// === Dirs + Files layout ===

file_store!(InstanceLayout, {
    dirs {
        memory: "memory",
        knowledge: "memory/knowledge",
        sessions: "memory/sessions",
        workspace: "workspace",
        data: "data",
    }
    files {
        settings: "settings.json",
        skill: "skill.md",
    }
});

// === Dirs only layout ===

file_store!(SimpleLayout, {
    dirs {
        logs: "logs",
        cache: "cache",
        temp: "tmp",
    }
});

// --- Tests ---

#[test]
fn test_new_and_base() {
    let layout = InstanceLayout::new("/data/instance-1");
    assert_eq!(layout.base(), std::path::Path::new("/data/instance-1"));
}

#[test]
fn test_dir_getters() {
    let layout = InstanceLayout::new("/data/inst");
    assert_eq!(layout.memory(), PathBuf::from("/data/inst/memory"));
    assert_eq!(layout.knowledge(), PathBuf::from("/data/inst/memory/knowledge"));
    assert_eq!(layout.sessions(), PathBuf::from("/data/inst/memory/sessions"));
    assert_eq!(layout.workspace(), PathBuf::from("/data/inst/workspace"));
    assert_eq!(layout.data(), PathBuf::from("/data/inst/data"));
}

#[test]
fn test_file_getters() {
    let layout = InstanceLayout::new("/data/inst");
    assert_eq!(layout.settings(), PathBuf::from("/data/inst/settings.json"));
    assert_eq!(layout.skill(), PathBuf::from("/data/inst/skill.md"));
}

#[test]
fn test_ensure_dirs() {
    let tmp = tempfile::tempdir().unwrap();
    let layout = InstanceLayout::new(tmp.path());
    uc(layout.ensure_dirs());

    assert!(tmp.path().join("memory").is_dir());
    assert!(tmp.path().join("memory/knowledge").is_dir());
    assert!(tmp.path().join("memory/sessions").is_dir());
    assert!(tmp.path().join("workspace").is_dir());
    assert!(tmp.path().join("data").is_dir());
}

#[test]
fn test_ensure_dirs_idempotent() {
    let tmp = tempfile::tempdir().unwrap();
    let layout = InstanceLayout::new(tmp.path());
    uc(layout.ensure_dirs());
    uc(layout.ensure_dirs()); // second call should not fail
    assert!(tmp.path().join("workspace").is_dir());
}

#[test]
fn test_dirs_only_layout() {
    let layout = SimpleLayout::new("/srv/app");
    assert_eq!(layout.logs(), PathBuf::from("/srv/app/logs"));
    assert_eq!(layout.cache(), PathBuf::from("/srv/app/cache"));
    assert_eq!(layout.temp(), PathBuf::from("/srv/app/tmp"));
}

#[test]
fn test_dirs_only_ensure() {
    let tmp = tempfile::tempdir().unwrap();
    let layout = SimpleLayout::new(tmp.path());
    uc(layout.ensure_dirs());

    assert!(tmp.path().join("logs").is_dir());
    assert!(tmp.path().join("cache").is_dir());
    assert!(tmp.path().join("tmp").is_dir());
}

#[test]
fn test_clone() {
    let layout = InstanceLayout::new("/data/inst");
    let cloned = layout.clone();
    assert_eq!(cloned.base(), layout.base());
    assert_eq!(cloned.workspace(), layout.workspace());
}

#[test]
fn test_debug() {
    let layout = InstanceLayout::new("/data/inst");
    let debug_str = format!("{:?}", layout);
    assert!(debug_str.contains("InstanceLayout"));
    assert!(debug_str.contains("/data/inst"));
}

#[test]
fn test_relative_base() {
    let layout = InstanceLayout::new("relative/path");
    assert_eq!(layout.memory(), PathBuf::from("relative/path/memory"));
    assert_eq!(layout.settings(), PathBuf::from("relative/path/settings.json"));
}

#[test]
fn test_files_not_created_by_ensure_dirs() {
    let tmp = tempfile::tempdir().unwrap();
    let layout = InstanceLayout::new(tmp.path());
    uc(layout.ensure_dirs());

    // ensure_dirs only creates directories, not files
    assert!(!tmp.path().join("settings.json").exists());
    assert!(!tmp.path().join("skill.md").exists());
}