//! Doctor command - checks system prerequisites and environment health

use anyhow::Result;
use colored::Colorize;
use std::process::Command;
use which::which;

/// Run the doctor command
pub async fn run(verbose: bool) -> Result<()> {
    println!("{}", "🩺 Running system health check...".bold());
    println!();

    let mut all_healthy = true;

    // Check required tools
    let required_tools = [
        ("docker", "Docker", "https://docs.docker.com/get-docker/"),
        ("docker-compose", "Docker Compose", "Included with Docker Desktop"),
        ("node", "Node.js", "https://nodejs.org/"),
        ("pnpm", "pnpm", "npm install -g pnpm"),
        ("python3", "Python 3", "https://python.org/"),
        ("git", "Git", "https://git-scm.com/"),
    ];

    println!("{}", "Required Tools:".bold().underline());
    for (cmd, name, install) in &required_tools {
        let status = check_tool(cmd);
        print_status(name, &status, verbose);
        if status.is_err() {
            all_healthy = false;
            if verbose {
                println!("  {} Install: {}", "→".dimmed(), install);
            }
        }
    }
    println!();

    // Check optional tools
    let optional_tools = [
        ("rustc", "Rust", "https://rustup.rs/"),
        ("just", "Just", "cargo install just"),
        ("sops", "SOPS", "https://github.com/getsops/sops"),
        ("age", "Age", "https://github.com/FiloSottile/age"),
        ("ollama", "Ollama", "https://ollama.com/"),
    ];

    println!("{}", "Optional Tools:".bold().underline());
    for (cmd, name, _) in &optional_tools {
        let status = check_tool(cmd);
        print_status(name, &status, verbose);
    }
    println!();

    // Check Docker daemon
    println!("{}", "Docker Status:".bold().underline());
    let docker_status = check_docker();
    print_status("Docker daemon", &docker_status, verbose);
    if docker_status.is_err() {
        all_healthy = false;
    }
    println!();

    // Check key ports
    println!("{}", "Port Availability:".bold().underline());
    let ports = [
        (3080, "LibreChat"),
        (7878, "Oxigraph"),
        (8080, "SEA API"),
        (11434, "Ollama"),
    ];
    for (port, name) in &ports {
        let status = check_port(*port);
        let display = format!("{} ({})", name, port);
        print_status(&display, &status, verbose);
    }
    println!();

    // Summary
    if all_healthy {
        println!(
            "{}",
            "✅ All required prerequisites are healthy!".green().bold()
        );
        println!();
        println!("Next steps:");
        println!("  {} Start services", "sea-forge up".cyan());
        println!("  {} Run spec validation", "sea-forge validate".cyan());
    } else {
        println!(
            "{}",
            "⚠️  Some prerequisites are missing.".yellow().bold()
        );
        println!("Run with {} for installation instructions.", "--verbose".cyan());
    }

    Ok(())
}

fn check_tool(cmd: &str) -> Result<String, String> {
    match which(cmd) {
        Ok(path) => {
            // Try to get version
            let version = Command::new(cmd)
                .arg("--version")
                .output()
                .ok()
                .and_then(|o| String::from_utf8(o.stdout).ok())
                .map(|s| s.lines().next().unwrap_or("").to_string())
                .unwrap_or_else(|| path.display().to_string());
            Ok(version.trim().to_string())
        }
        Err(_) => Err(format!("{} not found in PATH", cmd)),
    }
}

fn check_docker() -> Result<String, String> {
    let output = Command::new("docker")
        .args(["info", "--format", "{{.ServerVersion}}"])
        .output()
        .map_err(|e| e.to_string())?;

    if output.status.success() {
        let version = String::from_utf8_lossy(&output.stdout).trim().to_string();
        Ok(format!("Running ({})", version))
    } else {
        Err("Docker daemon not running".to_string())
    }
}

fn check_port(port: u16) -> Result<String, String> {
    use std::net::TcpListener;
    match TcpListener::bind(("127.0.0.1", port)) {
        Ok(_) => Ok("Available".to_string()),
        Err(_) => Err(format!("Port {} in use", port)),
    }
}

fn print_status(name: &str, status: &Result<String, String>, verbose: bool) {
    match status {
        Ok(info) => {
            let info_display = if verbose { format!(" ({})", info) } else { String::new() };
            println!("  {} {}{}", "✓".green(), name, info_display.dimmed());
        }
        Err(msg) => {
            let msg_display = if verbose { format!(" - {}", msg) } else { String::new() };
            println!("  {} {}{}", "✗".red(), name, msg_display.dimmed());
        }
    }
}
