use std::sync::OnceLock;

use super::Package;

const BUILTIN_TOMLS: &[(&str, &str)] = &[
    ("plannotator", include_str!("data/plannotator.toml")),
    ("context7", include_str!("data/context7.toml")),
    ("claude-mem", include_str!("data/claude-mem.toml")),
    ("seek", include_str!("data/seek.toml")),
    ("fff-nvim", include_str!("data/fff-nvim.toml")),
    ("ccstatusline", include_str!("data/ccstatusline.toml")),
    ("temporal", include_str!("data/temporal.toml")),
    (
        "modern-web-guidance",
        include_str!("data/modern-web-guidance.toml"),
    ),
    ("serena", include_str!("data/serena.toml")),
];

static CACHE: OnceLock<Vec<Package>> = OnceLock::new();

fn parsed() -> &'static Vec<Package> {
    CACHE.get_or_init(|| {
        BUILTIN_TOMLS
            .iter()
            .map(|(slot, text)| {
                let package: Package = toml::from_str(text)
                    .unwrap_or_else(|e| panic!("built-in package '{slot}' invalid: {e}"));
                assert_eq!(
                    package.name, *slot,
                    "built-in {slot}.toml name field does not match filename"
                );
                package
            })
            .collect()
    })
}

pub fn all() -> Vec<Package> {
    parsed().clone()
}

pub fn by_name(name: &str) -> Option<Package> {
    parsed().iter().find(|p| p.name == name).cloned()
}

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

    #[test]
    fn all_builtins_parse() {
        let packages = all();
        assert_eq!(packages.len(), BUILTIN_TOMLS.len());
    }

    #[test]
    fn each_builtin_by_name() {
        for (slot, _) in BUILTIN_TOMLS {
            assert!(by_name(slot).is_some(), "missing built-in {slot}");
        }
    }

    #[test]
    fn seek_has_prereq_and_two_install_methods() {
        let e = by_name("seek").unwrap();
        assert_eq!(e.install.len(), 2);
        assert_eq!(e.prereq.len(), 1);
        assert_eq!(e.prereq[0].name, "universal-ctags");
        assert!(e.prereq[0].install.contains_key("pacman"));
    }

    #[test]
    fn context7_declares_skill_and_rule_artifacts() {
        let e = by_name("context7").unwrap();
        assert!(e.verify.is_none(), "verify replaced by artifacts");
        assert_eq!(e.artifacts.len(), 2);
        use crate::registry::Artifact;
        assert!(matches!(
            &e.artifacts[0],
            Artifact::ClaudeSkill { name } if name == "find-docs"
        ));
        assert!(matches!(
            &e.artifacts[1],
            Artifact::ClaudeRule { name } if name == "context7"
        ));
    }

    #[test]
    fn ccstatusline_uses_bunx_and_path_artifact() {
        let e = by_name("ccstatusline").unwrap();
        assert_eq!(e.scope, super::super::Scope::Global);
        assert_eq!(e.install.len(), 1);
        assert_eq!(e.install[0].command, "bunx -y ccstatusline@latest");
        assert!(e.install[0].interactive);
        assert_eq!(e.prereq.len(), 1);
        assert_eq!(e.prereq[0].name, "bun");
        use crate::registry::Artifact;
        assert!(matches!(
            &e.artifacts[0],
            Artifact::Path { path } if path == "~/.config/ccstatusline/settings.json"
        ));
    }

    #[test]
    fn modern_web_guidance_is_a_claude_skill() {
        let e = by_name("modern-web-guidance").unwrap();
        assert_eq!(e.install.len(), 1);
        assert_eq!(
            e.install[0].command,
            "npx modern-web-guidance@latest install"
        );
        assert!(e.install[0].interactive);
        assert_eq!(
            e.update.as_deref(),
            Some("npx modern-web-guidance@latest update")
        );
        use crate::registry::Artifact;
        assert!(matches!(
            &e.artifacts[0],
            Artifact::ClaudeSkill { name } if name == "modern-web-guidance"
        ));
    }

    #[test]
    fn serena_is_an_mcp_server_with_uv_prereq() {
        let e = by_name("serena").unwrap();
        assert_eq!(e.scope, super::super::Scope::Global);
        assert_eq!(e.install.len(), 1);
        assert!(e.install[0].command.starts_with("uv tool install"));
        assert!(e.install[0]
            .command
            .contains("claude mcp add --scope user serena"));
        assert!(e.prereq.iter().any(|p| p.name == "uv"));
        assert!(e.prereq.iter().any(|p| p.name == "claude"));
        use crate::registry::Artifact;
        assert!(matches!(
            &e.artifacts[0],
            Artifact::Cli { name } if name == "serena"
        ));
    }

    #[test]
    fn all_builtins_migrated_off_verify() {
        for e in all() {
            assert!(
                e.verify.is_none(),
                "package `{}` still uses `verify`; migrate to `[[artifacts]]`",
                e.name
            );
        }
    }
}
