//! Apply command - converge to declared state
//!
//! Reads sea-forge.yaml and ensures all services match the declared state.

use anyhow::{Context, Result};
use colored::Colorize;
use std::process::Command;

use crate::config::{ForgeConfig, ServiceState};
use crate::state::{find_project_root, ComponentStatus, ForgeState};

/// Run the apply command
pub async fn run(dry_run: bool, _force: bool) -> Result<()> {
    println!("{}", "🔄 Applying declared state...".bold());
    println!();

    let project_root = find_project_root()?;
    let config = ForgeConfig::load(&project_root)?;
    let mut state = ForgeState::load(&project_root)?;

    // Show current config
    println!("{}", "Declared services:".dimmed());
    for (name, svc) in &config.services {
        let state_icon = match svc.state {
            ServiceState::Running => "▶".green(),
            ServiceState::Stopped => "⏹".red(),
            ServiceState::Optional => "?".yellow(),
        };
        let auto_heal = if svc.auto_restart { " (auto-heal)" } else { "" };
        println!(
            "  {} {} {:?}{}",
            state_icon,
            name,
            svc.state,
            auto_heal.dimmed()
        );
    }
    println!();

    if dry_run {
        println!("{} Would converge to declared state", "[DRY-RUN]".cyan());
        return Ok(());
    }

    // Converge each service
    for (name, svc) in &config.services {
        let current_status = check_service_status(name);

        match (&svc.state, current_status) {
            (ServiceState::Running, false) => {
                println!("{} Starting {}...", "→".cyan(), name);
                start_service(name, &project_root)?;
                state.set_component(name, "running", ComponentStatus::Running);
            }
            (ServiceState::Stopped, true) => {
                println!("{} Stopping {}...", "→".cyan(), name);
                stop_service(name, &project_root)?;
                state.set_component(name, "stopped", ComponentStatus::Stopped);
            }
            (ServiceState::Running, true) => {
                println!("{} {} already running", "✓".green(), name);
            }
            (ServiceState::Stopped, false) => {
                println!("{} {} already stopped", "✓".green(), name);
            }
            (ServiceState::Optional, _) => {
                println!("{} {} is optional, skipping", "○".dimmed(), name);
            }
        }
    }

    // Save state
    state.last_run = Some(chrono::Utc::now().to_rfc3339());
    state.save(&project_root)?;

    println!();
    println!("{}", "✅ State applied!".green().bold());

    Ok(())
}

fn check_service_status(name: &str) -> bool {
    let health_urls = [
        ("librechat", "http://localhost:3080"),
        ("oxigraph", "http://localhost:7878"),
        ("sea-api", "http://localhost:8080/health"),
        ("opa", "http://localhost:8181/health"),
        ("ollama", "http://localhost:11434/api/tags"),
    ];

    for (svc, url) in &health_urls {
        if name == *svc {
            return Command::new("curl")
                .args(["-sf", "--max-time", "2", url])
                .output()
                .map(|o| o.status.success())
                .unwrap_or(false);
        }
    }

    false
}

fn start_service(name: &str, project_root: &std::path::Path) -> Result<()> {
    let librechat_dir = project_root.join("deploy/librechat");

    match name {
        "librechat" | "sea-api" | "mongodb" | "meilisearch" => {
            Command::new("docker")
                .args(["compose", "up", "-d", name])
                .current_dir(&librechat_dir)
                .output()
                .context(format!("Failed to start {}", name))?;
        }
        "oxigraph" | "opa" => {
            Command::new("just")
                .arg("skeleton-up")
                .current_dir(project_root)
                .output()
                .context("Failed to start skeleton services")?;
        }
        _ => {
            println!("  {} Unknown service: {}", "⚠".yellow(), name);
        }
    }

    Ok(())
}

fn stop_service(name: &str, project_root: &std::path::Path) -> Result<()> {
    let librechat_dir = project_root.join("deploy/librechat");

    match name {
        "librechat" | "sea-api" | "mongodb" | "meilisearch" => {
            Command::new("docker")
                .args(["compose", "stop", name])
                .current_dir(&librechat_dir)
                .output()
                .context(format!("Failed to stop {}", name))?;
        }
        "oxigraph" | "opa" => {
            Command::new("just")
                .arg("skeleton-down")
                .current_dir(project_root)
                .output()
                .context("Failed to stop skeleton services")?;
        }
        _ => {}
    }

    Ok(())
}
