pub mod builtin;
pub mod custom;

use std::collections::BTreeMap;

use anyhow::Result;
use serde::{Deserialize, Serialize};

use crate::registry::{Artifact, InstallMethod, Prereq, Source};
use crate::tools::Tool;

/// An installable AI tool runtime (claude, codex, cursor, …). Distinct from
/// `Package`, which is an add-on that wires *into* a runtime.
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ToolDef {
    pub name: String,
    #[serde(default)]
    pub description: Option<String>,
    #[serde(default)]
    pub homepage: Option<String>,
    /// Optional binding to one of the closed `Tool` enum variants — when set,
    /// installing this entry satisfies that runtime.
    #[serde(default)]
    pub runtime: Option<Tool>,
    #[serde(default)]
    pub artifacts: Vec<Artifact>,
    #[serde(default)]
    pub verify: Option<String>,
    #[serde(default)]
    pub prereq: Vec<Prereq>,
    #[serde(default)]
    pub install: Vec<InstallMethod>,
    #[serde(default)]
    pub update: Option<String>,
    #[serde(default)]
    pub uninstall: Option<String>,
}

#[derive(Debug, Clone)]
pub struct Resolved {
    pub tool: ToolDef,
    pub source: Source,
}

pub fn list() -> Result<Vec<Resolved>> {
    let mut by_name: BTreeMap<String, Resolved> = BTreeMap::new();
    for tool in builtin::all() {
        by_name.insert(
            tool.name.clone(),
            Resolved {
                tool,
                source: Source::BuiltIn,
            },
        );
    }
    for tool in custom::load_all()? {
        let source = if by_name.contains_key(&tool.name) {
            Source::CustomOverride
        } else {
            Source::Custom
        };
        by_name.insert(tool.name.clone(), Resolved { tool, source });
    }
    Ok(by_name.into_values().collect())
}

pub fn resolve(name: &str) -> Result<Option<Resolved>> {
    if let Some(tool) = custom::load(name)? {
        let source = if builtin::by_name(name).is_some() {
            Source::CustomOverride
        } else {
            Source::Custom
        };
        return Ok(Some(Resolved { tool, source }));
    }
    if let Some(tool) = builtin::by_name(name) {
        return Ok(Some(Resolved {
            tool,
            source: Source::BuiltIn,
        }));
    }
    Ok(None)
}
