//! `fxt cluster` -- cluster operations.

use anyhow::Result;
use clap::{Parser, Subcommand};
use colored::Colorize;
use serde::Serialize;

use crate::grpc::FoxhuntClient;
use crate::output::{self, HumanReadable, OutputFormat};

#[derive(Parser, Debug)]
pub struct ClusterCommand {
    #[command(subcommand)]
    action: ClusterAction,
}

#[derive(Subcommand, Debug)]
enum ClusterAction {
    /// Show cluster resource utilization
    Status,
    /// Show node and pod resource details
    Resources,
    /// Show recent cluster events
    Events {
        /// Maximum number of events to show
        #[arg(long, default_value = "50")]
        limit: u32,
    },
}

// ── Result types ──────────────────────────────────────────────────────

#[derive(Serialize)]
struct ServiceInfo {
    name: String,
    health: String,
    state: String,
    version: String,
    uptime_seconds: i64,
    error_message: String,
}

#[derive(Serialize)]
struct ClusterStatusResult {
    overall_health: String,
    healthy_services: i32,
    total_services: i32,
    critical_issues: Vec<String>,
    system_uptime_seconds: i64,
    cpu_usage_pct: f64,
    memory_usage_pct: f64,
    disk_usage_pct: f64,
    error_rate_pct: f64,
    services: Vec<ServiceInfo>,
}

impl HumanReadable for ClusterStatusResult {
    fn print_human(&self) {
        let health_colored = match self.overall_health.as_str() {
            "HEALTHY" => self.overall_health.green().bold().to_string(),
            "DEGRADED" => self.overall_health.yellow().bold().to_string(),
            "UNHEALTHY" | "CRITICAL" => self.overall_health.red().bold().to_string(),
            _ => self.overall_health.clone(),
        };
        println!("{} {health_colored}", "Cluster Status:".cyan().bold());
        println!(
            "  Services: {}/{} healthy",
            self.healthy_services, self.total_services,
        );
        #[allow(clippy::integer_division)]
        let uptime_hours = self.system_uptime_seconds / 3600;
        println!("  Uptime:   {uptime_hours} hours");
        println!("  CPU:      {:.1}%", self.cpu_usage_pct);
        println!("  Memory:   {:.1}%", self.memory_usage_pct);
        println!("  Disk:     {:.1}%", self.disk_usage_pct);
        println!("  Errors:   {:.2}%", self.error_rate_pct);

        if !self.critical_issues.is_empty() {
            println!();
            println!("{}", "Critical Issues:".red().bold());
            for issue in &self.critical_issues {
                println!("  - {}", issue.red());
            }
        }

        if !self.services.is_empty() {
            println!();
            println!("{}", "Services:".cyan().bold());
            for svc in &self.services {
                let health_str = match svc.health.as_str() {
                    "HEALTHY" => svc.health.green().to_string(),
                    "DEGRADED" => svc.health.yellow().to_string(),
                    "UNHEALTHY" | "CRITICAL" => svc.health.red().to_string(),
                    _ => svc.health.clone(),
                };
                let version_str = if svc.version.is_empty() {
                    String::new()
                } else {
                    format!(" v{}", svc.version)
                };
                println!(
                    "  [{}] {}{} ({})",
                    health_str, svc.name, version_str, svc.state,
                );
                if !svc.error_message.is_empty() {
                    println!("         {}", svc.error_message.red());
                }
            }
        }
    }
}

#[derive(Serialize)]
struct MetricInfo {
    name: String,
    value: f64,
    unit: String,
}

#[derive(Serialize)]
struct ResourcesResult {
    metrics: Vec<MetricInfo>,
}

impl HumanReadable for ResourcesResult {
    fn print_human(&self) {
        println!("{}", "Cluster Resources".cyan().bold());
        if self.metrics.is_empty() {
            println!("  No resource metrics available.");
            return;
        }
        for m in &self.metrics {
            let unit_suffix = if m.unit.is_empty() {
                String::new()
            } else {
                format!(" {}", m.unit)
            };
            println!("  {:30} {:>10.2}{}", m.name, m.value, unit_suffix);
        }
    }
}

#[derive(Serialize)]
struct EventsResult {
    message: String,
}

impl HumanReadable for EventsResult {
    fn print_human(&self) {
        println!("{}", "Cluster Events".cyan().bold());
        println!("  {}", self.message);
    }
}

// ── Helpers ───────────────────────────────────────────────────────────

fn system_health_name(value: i32) -> &'static str {
    use crate::proto::monitoring::SystemHealth;
    match SystemHealth::try_from(value) {
        Ok(SystemHealth::Healthy) => "HEALTHY",
        Ok(SystemHealth::Degraded) => "DEGRADED",
        Ok(SystemHealth::Unhealthy) => "UNHEALTHY",
        Ok(SystemHealth::Critical) => "CRITICAL",
        _ => "UNKNOWN",
    }
}

fn service_health_name(value: i32) -> &'static str {
    use crate::proto::monitoring::ServiceHealth;
    match ServiceHealth::try_from(value) {
        Ok(ServiceHealth::Healthy) => "HEALTHY",
        Ok(ServiceHealth::Degraded) => "DEGRADED",
        Ok(ServiceHealth::Unhealthy) => "UNHEALTHY",
        Ok(ServiceHealth::Critical) => "CRITICAL",
        _ => "UNKNOWN",
    }
}

fn service_state_name(value: i32) -> &'static str {
    use crate::proto::monitoring::ServiceState;
    match ServiceState::try_from(value) {
        Ok(ServiceState::Starting) => "starting",
        Ok(ServiceState::Running) => "running",
        Ok(ServiceState::Stopping) => "stopping",
        Ok(ServiceState::Stopped) => "stopped",
        Ok(ServiceState::Error) => "error",
        _ => "unknown",
    }
}

// ── Execute ───────────────────────────────────────────────────────────

impl ClusterCommand {
    pub async fn execute(&self, client: &FoxhuntClient, format: OutputFormat) -> Result<()> {
        match &self.action {
            ClusterAction::Status => {
                let resp = client
                    .monitoring()
                    .get_system_status(crate::proto::monitoring::GetSystemStatusRequest {
                        service_names: vec![],
                    })
                    .await?
                    .into_inner();

                let overall = resp.overall_status.unwrap_or_default();
                let sys_metrics = overall.system_metrics.unwrap_or_default();

                let services: Vec<ServiceInfo> = resp
                    .service_statuses
                    .iter()
                    .map(|s| ServiceInfo {
                        name: s.service_name.clone(),
                        health: service_health_name(s.health).to_owned(),
                        state: service_state_name(s.state).to_owned(),
                        version: s.version.clone().unwrap_or_default(),
                        uptime_seconds: s.uptime_seconds,
                        error_message: s.error_message.clone().unwrap_or_default(),
                    })
                    .collect();

                let result = ClusterStatusResult {
                    overall_health: system_health_name(overall.overall_health).to_owned(),
                    healthy_services: overall.healthy_services,
                    total_services: overall.total_services,
                    critical_issues: overall.critical_issues,
                    system_uptime_seconds: overall.system_uptime_seconds,
                    cpu_usage_pct: sys_metrics.cpu_usage_percent,
                    memory_usage_pct: sys_metrics.memory_usage_percent,
                    disk_usage_pct: sys_metrics.disk_usage_percent,
                    error_rate_pct: sys_metrics.error_rate_percent,
                    services,
                };
                output::render(&result, format)?;
            }

            ClusterAction::Resources => {
                let resource_names = vec![
                    "cpu.usage".to_owned(),
                    "memory.usage".to_owned(),
                    "disk.usage".to_owned(),
                    "gpu.utilization".to_owned(),
                    "gpu.memory.used".to_owned(),
                    "network.io".to_owned(),
                ];

                let resp = client
                    .monitoring()
                    .get_metrics(crate::proto::monitoring::GetMetricsRequest {
                        metric_names: resource_names,
                        start_time: None,
                        end_time: None,
                        aggregation: None,
                    })
                    .await?
                    .into_inner();

                let metrics: Vec<MetricInfo> = resp
                    .metrics
                    .iter()
                    .map(|m| MetricInfo {
                        name: m.name.clone(),
                        value: m.value,
                        unit: m.unit.clone(),
                    })
                    .collect();

                let result = ResourcesResult { metrics };
                output::render(&result, format)?;
            }

            ClusterAction::Events { limit: _ } => {
                let result = EventsResult {
                    message: "Cluster events require kubectl access.  Use `kubectl get events --sort-by=.metadata.creationTimestamp` for direct access.".to_owned(),
                };
                output::render(&result, format)?;
            }
        }
        Ok(())
    }
}
