//! Claude Desktop local config reader.
//!
//! Claude Desktop is the most common entry point for new MCP users on
//! macOS. Reading its `claude_desktop_config.json` lets `mcptest servers`
//! show the same list the user already sees in the Desktop app, so the
//! "first passing test in five minutes" demo works without any YAML.
//!
//! Locations (per  acceptance criteria):
//!
//! | OS | Path |
//! | -- | ---- |
//! | macOS | `~/Library/Application Support/Claude/claude_desktop_config.json` |
//! | Windows | `%APPDATA%\Claude\claude_desktop_config.json` |
//! | Linux | `~/.config/Claude/claude_desktop_config.json` |
//!
//! Read-only: this module never writes to the config.

use std::path::PathBuf;

use super::{parse_mcp_servers_file, ClientSource, DiscoveredServer, DiscoveryError};

/// Discover servers declared in Claude Desktop's config file.
///
/// Returns an empty vec when the file does not exist (the user does not
/// have Claude Desktop installed, or has not configured any servers
/// yet). Returns a [`DiscoveryError`] when the file exists but cannot be
/// read or parsed.
pub fn discover_servers() -> Result<Vec<DiscoveredServer>, DiscoveryError> {
    let Some(path) = config_path()? else {
        return Ok(Vec::new());
    };
    parse_mcp_servers_file(&path, ClientSource::ClaudeDesktop)
}

/// Resolve the platform-specific Claude Desktop config path.
///
/// `Ok(None)` means we know the lookup convention for the current OS but
/// the user does not have the relevant env var (`HOME`, `APPDATA`) set,
/// which is common in CI containers. We treat that as "no config" rather
/// than a hard error so the rest of the walk can continue.
pub(super) fn config_path() -> Result<Option<PathBuf>, DiscoveryError> {
    if cfg!(target_os = "macos") {
        let Some(home) = home_dir() else {
            return Ok(None);
        };
        Ok(Some(
            home.join("Library")
                .join("Application Support")
                .join("Claude")
                .join("claude_desktop_config.json"),
        ))
    } else if cfg!(target_os = "windows") {
        match std::env::var_os("APPDATA") {
            Some(appdata) => Ok(Some(
                PathBuf::from(appdata)
                    .join("Claude")
                    .join("claude_desktop_config.json"),
            )),
            None => Ok(None),
        }
    } else {
        // Linux and other Unix variants.
        let Some(home) = home_dir() else {
            return Ok(None);
        };
        Ok(Some(
            home.join(".config")
                .join("Claude")
                .join("claude_desktop_config.json"),
        ))
    }
}

/// Read `HOME` directly so tests can override it deterministically.
///
/// We could call `etcetera::home_dir`, but on Unix it just reads `HOME`
/// anyway, and going direct keeps the test harness behavior obvious
/// (set `HOME=<tempdir>` and the loader follows).
fn home_dir() -> Option<PathBuf> {
    std::env::var_os("HOME").map(PathBuf::from)
}
