//! Detection of Zed editor installations on the current system.
//!
//! Checks for the existence of Zed configuration directories and discovers
//! configured agents within each installation.

use crate::agents::{self, ZedAgent};
use crate::paths::{self, ZedChannel};
use std::path::PathBuf;

/// A detected Zed editor installation with its configuration and agents.
#[derive(Debug, Clone)]
pub struct ZedInstallation {
    /// Release channel (Stable, Preview, Nightly, Dev).
    pub channel: ZedChannel,
    /// Path to the configuration directory (contains `settings.json`).
    pub config_dir: PathBuf,
    /// Path to the data directory (contains `external_agents/`).
    pub data_dir: PathBuf,
    /// Agents discovered from settings and resolved binary paths.
    pub agents: Vec<ZedAgent>,
}

/// Detect Zed installations on this system.
///
/// Checks for the existence of `settings.json` in the Zed config directory.
/// Currently, Zed uses a single config directory for all channels (unlike
/// some editors that have per-channel directories). We report one installation
/// per detected config directory.
///
/// For each found installation:
/// 1. Discovers agents from `settings.json` (`agent_servers` key)
/// 2. Resolves downloaded binary paths from the data directory
pub fn detect_installations() -> Vec<ZedInstallation> {
    let config_dir = match paths::zed_config_dir() {
        Some(d) => d,
        None => {
            tracing::debug!("Could not determine Zed config directory for this platform");
            return Vec::new();
        }
    };

    let data_dir = match paths::zed_data_dir() {
        Some(d) => d,
        None => {
            tracing::debug!("Could not determine Zed data directory for this platform");
            return Vec::new();
        }
    };

    let settings_path = config_dir.join("settings.json");
    if !settings_path.exists() {
        tracing::debug!(
            path = %settings_path.display(),
            "Zed settings.json not found — no installation detected"
        );
        return Vec::new();
    }

    tracing::info!(
        config = %config_dir.display(),
        data = %data_dir.display(),
        "Detected Zed installation"
    );

    let mut found_agents = agents::discover_agents_from_settings(&config_dir);

    // Also discover agents from external_agents/ that aren't in settings.
    let extra_agents = agents::discover_agents_from_external_dir(&data_dir, &found_agents);
    found_agents.extend(extra_agents);

    agents::resolve_binary_paths(&mut found_agents, &data_dir);

    // Enrich agents with registry metadata (display names, descriptions, args, icons).
    let registry = crate::registry::parse_registry(&data_dir);
    agents::enrich_agents_from_registry(&mut found_agents, &registry);

    // Zed currently uses a single config dir for all channels. We report it
    // as Stable by default. If we later learn how to distinguish channels
    // (e.g., via a marker file or binary path), we can refine this.
    vec![ZedInstallation {
        channel: ZedChannel::Stable,
        config_dir,
        data_dir,
        agents: found_agents,
    }]
}

/// Detect installations using explicit paths (useful for testing or overrides).
pub fn detect_installation_at(config_dir: PathBuf, data_dir: PathBuf) -> Option<ZedInstallation> {
    let settings_path = config_dir.join("settings.json");
    if !settings_path.exists() {
        return None;
    }

    let mut found_agents = agents::discover_agents_from_settings(&config_dir);
    let extra_agents = agents::discover_agents_from_external_dir(&data_dir, &found_agents);
    found_agents.extend(extra_agents);
    agents::resolve_binary_paths(&mut found_agents, &data_dir);

    let registry = crate::registry::parse_registry(&data_dir);
    agents::enrich_agents_from_registry(&mut found_agents, &registry);

    Some(ZedInstallation {
        channel: ZedChannel::Stable,
        config_dir,
        data_dir,
        agents: found_agents,
    })
}

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

    #[test]
    fn test_detect_installation_at_missing_settings() {
        let dir = tempfile::tempdir().unwrap();
        let result = detect_installation_at(dir.path().to_path_buf(), dir.path().to_path_buf());
        assert!(result.is_none());
    }

    #[test]
    fn test_detect_installation_at_with_settings() {
        let config_dir = tempfile::tempdir().unwrap();
        let data_dir = tempfile::tempdir().unwrap();

        let settings = r#"{
            "agent_servers": {
                "claude-acp": { "type": "registry" }
            }
        }"#;
        std::fs::write(config_dir.path().join("settings.json"), settings).unwrap();

        let installation =
            detect_installation_at(config_dir.path().to_path_buf(), data_dir.path().to_path_buf());
        assert!(installation.is_some());

        let inst = installation.unwrap();
        assert_eq!(inst.channel, ZedChannel::Stable);
        assert_eq!(inst.agents.len(), 1);
        assert_eq!(inst.agents[0].name, "claude-acp");
    }

    #[test]
    fn test_detect_installation_at_empty_settings() {
        let config_dir = tempfile::tempdir().unwrap();
        let data_dir = tempfile::tempdir().unwrap();

        std::fs::write(config_dir.path().join("settings.json"), "{}").unwrap();

        let installation =
            detect_installation_at(config_dir.path().to_path_buf(), data_dir.path().to_path_buf());
        assert!(installation.is_some());

        let inst = installation.unwrap();
        assert!(inst.agents.is_empty());
    }
}
