//! Diagnostics command handlers
//!
//! Orchestrates real-time diagnostic monitoring and export.

use crate::commands::diagnostics::{DiagnosticsCommand, DiagnosticsFilter};
use crate::context::CliContext;
use anyhow::{Context as AnyhowContext, Result};
use console::{style, Term};
use mecha10_core::prelude::*;
use mecha10_core::topics::Topic;
use mecha10_diagnostics::prelude::*;
use serde_json;
use std::collections::HashMap;
use std::fs;
use std::time::Duration;
use tokio::time;

/// Handle diagnostics command
pub async fn handle_diagnostics(ctx: &mut CliContext, command: &DiagnosticsCommand) -> Result<()> {
    match command {
        DiagnosticsCommand::Watch { filter, interval } => handle_watch(ctx, filter, *interval).await,
        DiagnosticsCommand::Export { output, duration } => handle_export(ctx, output, *duration).await,
    }
}

/// Handle watch command - live diagnostics dashboard
async fn handle_watch(_ctx: &CliContext, filter: &DiagnosticsFilter, interval_secs: u64) -> Result<()> {
    let term = Term::stdout();
    term.clear_screen()?;

    println!("{}", style("📊 Mecha10 Diagnostics").bold().cyan());
    println!("{}", style("━".repeat(60)).dim());
    println!();

    // Create Mecha10 context for pub/sub
    let mecha10_ctx = Context::new("mecha10-cli-diagnostics")
        .await
        .context("Failed to connect to Redis. Is the control plane running?")?;

    // Subscribe to diagnostic topics based on filter
    let mut streaming_rx = if filter.show_streaming() {
        Some(
            mecha10_ctx
                .subscribe(Topic::<DiagnosticMessage<StreamingPipelineMetrics>>::new(
                    TOPIC_DIAGNOSTICS_STREAMING_PIPELINE,
                ))
                .await?,
        )
    } else {
        None
    };

    let mut godot_rx = if filter.show_godot() {
        Some(
            mecha10_ctx
                .subscribe(Topic::<DiagnosticMessage<GodotConnectionMetrics>>::new(
                    TOPIC_DIAGNOSTICS_GODOT_CONNECTION,
                ))
                .await?,
        )
    } else {
        None
    };

    let mut docker_rx = if filter.show_docker() {
        Some(
            mecha10_ctx
                .subscribe(Topic::<DiagnosticMessage<DockerContainerMetrics>>::new(
                    TOPIC_DIAGNOSTICS_DOCKER_CONTAINERS,
                ))
                .await?,
        )
    } else {
        None
    };

    let mut system_rx = if filter.show_system() {
        Some(
            mecha10_ctx
                .subscribe(Topic::<DiagnosticMessage<SystemResourceMetrics>>::new(
                    TOPIC_DIAGNOSTICS_SYSTEM_RESOURCES,
                ))
                .await?,
        )
    } else {
        None
    };

    println!("{}", style("Waiting for diagnostic data...").dim());
    println!("{}", style("Press Ctrl+C to exit").dim());
    println!();

    // State for latest metrics
    let mut latest_streaming: Option<DiagnosticMessage<StreamingPipelineMetrics>> = None;
    let mut latest_godot: Option<DiagnosticMessage<GodotConnectionMetrics>> = None;
    let mut latest_docker: Vec<DiagnosticMessage<DockerContainerMetrics>> = Vec::new();
    let mut latest_system: Option<DiagnosticMessage<SystemResourceMetrics>> = None;

    let mut display_interval = time::interval(Duration::from_secs(interval_secs));

    loop {
        tokio::select! {
            // Receive streaming metrics
            Some(msg) = async {
                if let Some(rx) = &mut streaming_rx {
                    rx.recv().await
                } else {
                    None
                }
            } => {
                latest_streaming = Some(msg);
            }

            // Receive Godot metrics
            Some(msg) = async {
                if let Some(rx) = &mut godot_rx {
                    rx.recv().await
                } else {
                    None
                }
            } => {
                latest_godot = Some(msg);
            }

            // Receive Docker metrics
            Some(msg) = async {
                if let Some(rx) = &mut docker_rx {
                    rx.recv().await
                } else {
                    None
                }
            } => {
                // Update or append to docker metrics
                if let Some(idx) = latest_docker.iter().position(|m| m.payload.container_name == msg.payload.container_name) {
                    latest_docker[idx] = msg;
                } else {
                    latest_docker.push(msg);
                }
            }

            // Receive system metrics
            Some(msg) = async {
                if let Some(rx) = &mut system_rx {
                    rx.recv().await
                } else {
                    None
                }
            } => {
                latest_system = Some(msg);
            }

            // Refresh display
            _ = display_interval.tick() => {
                term.clear_screen()?;
                display_diagnostics(&latest_streaming, &latest_godot, &latest_docker, &latest_system)?;
            }
        }
    }
}

/// Display diagnostics in formatted output
fn display_diagnostics(
    streaming: &Option<DiagnosticMessage<StreamingPipelineMetrics>>,
    godot: &Option<DiagnosticMessage<GodotConnectionMetrics>>,
    docker: &[DiagnosticMessage<DockerContainerMetrics>],
    system: &Option<DiagnosticMessage<SystemResourceMetrics>>,
) -> Result<()> {
    println!("{}", style("📊 Mecha10 Diagnostics").bold().cyan());
    println!("{}", style("━".repeat(60)).dim());
    println!();

    // Streaming Pipeline Metrics
    if let Some(msg) = streaming {
        println!("{}", style("🎥 Streaming Pipeline").bold().green());
        println!("  FPS:            {:.1}", msg.payload.fps);
        println!("  Frames Received: {}", msg.payload.frames_received);
        println!("  Frames Encoded:  {}", msg.payload.frames_encoded);
        println!("  Frames Sent:     {}", msg.payload.frames_sent);
        println!(
            "  Frames Dropped:  {} {}",
            msg.payload.frames_dropped,
            if msg.payload.frames_dropped > 0 {
                style("⚠").yellow()
            } else {
                style("✓").green()
            }
        );
        println!("  Bandwidth:       {:.1} Kbps", msg.payload.bytes_per_second / 1024);
        println!();
    }

    // Godot Connection Metrics
    if let Some(msg) = godot {
        println!("{}", style("🎮 Godot Simulation").bold().blue());
        println!(
            "  Control:  {} (uptime: {}s, reconnects: {})",
            if msg.payload.control_connected {
                style("🟢 Connected").green()
            } else {
                style("🔴 Disconnected").red()
            },
            msg.payload.control_uptime_seconds,
            msg.payload.control_reconnects
        );
        println!(
            "  Camera:   {} (uptime: {}s, reconnects: {})",
            if msg.payload.camera_connected {
                style("🟢 Connected").green()
            } else {
                style("🔴 Disconnected").red()
            },
            msg.payload.camera_uptime_seconds,
            msg.payload.camera_reconnects
        );
        println!("  Last control msg: {}µs ago", msg.payload.last_control_message_us);
        println!("  Last camera frame: {}µs ago", msg.payload.last_camera_frame_us);
        println!();
    }

    // Docker Container Metrics
    if !docker.is_empty() {
        println!("{}", style("🐳 Docker Containers").bold().magenta());
        for msg in docker {
            println!(
                "  {} - CPU: {:.1}%, Mem: {:.1} MB",
                style(&msg.payload.container_name).bold(),
                msg.payload.cpu_percent,
                msg.payload.memory_usage_bytes as f64 / 1_048_576.0
            );
        }
        println!();
    }

    // System Resource Metrics
    if let Some(msg) = system {
        println!("{}", style("💻 System Resources").bold().yellow());
        println!("  CPU:    {:.1}%", msg.payload.cpu_percent);
        println!(
            "  Memory: {:.1} MB / {:.1} MB ({:.1}%)",
            msg.payload.memory_used_bytes as f64 / 1_048_576.0,
            msg.payload.memory_total_bytes as f64 / 1_048_576.0,
            msg.payload.memory_percent
        );
        println!(
            "  Disk:   {:.1} GB / {:.1} GB ({:.1}%)",
            msg.payload.disk_used_bytes as f64 / 1_073_741_824.0,
            msg.payload.disk_total_bytes as f64 / 1_073_741_824.0,
            msg.payload.disk_percent
        );
        println!();
    }

    println!("{}", style("Press Ctrl+C to exit").dim());

    Ok(())
}

/// Handle export command - snapshot diagnostics to file
async fn handle_export(_ctx: &CliContext, output: &str, duration_secs: u64) -> Result<()> {
    println!("{}", style("📊 Exporting Diagnostics").bold().cyan());
    println!("{}", style("━".repeat(60)).dim());
    println!();

    // Create Mecha10 context for pub/sub
    let mecha10_ctx = Context::new("mecha10-cli-diagnostics-export")
        .await
        .context("Failed to connect to Redis. Is the control plane running?")?;

    // Subscribe to all diagnostic topics
    let mut streaming_rx = mecha10_ctx
        .subscribe(Topic::<DiagnosticMessage<StreamingPipelineMetrics>>::new(
            TOPIC_DIAGNOSTICS_STREAMING_PIPELINE,
        ))
        .await?;

    let mut godot_rx = mecha10_ctx
        .subscribe(Topic::<DiagnosticMessage<GodotConnectionMetrics>>::new(
            TOPIC_DIAGNOSTICS_GODOT_CONNECTION,
        ))
        .await?;

    let mut docker_rx = mecha10_ctx
        .subscribe(Topic::<DiagnosticMessage<DockerContainerMetrics>>::new(
            TOPIC_DIAGNOSTICS_DOCKER_CONTAINERS,
        ))
        .await?;

    let mut system_rx = mecha10_ctx
        .subscribe(Topic::<DiagnosticMessage<SystemResourceMetrics>>::new(
            TOPIC_DIAGNOSTICS_SYSTEM_RESOURCES,
        ))
        .await?;

    println!("Collecting metrics for {} seconds...", duration_secs);

    // Collect all metrics for specified duration
    let mut streaming_metrics = Vec::new();
    let mut godot_metrics = Vec::new();
    let mut docker_metrics = Vec::new();
    let mut system_metrics = Vec::new();

    let start = std::time::Instant::now();
    let timeout = Duration::from_secs(duration_secs);

    while start.elapsed() < timeout {
        tokio::select! {
            Some(msg) = streaming_rx.recv() => {
                streaming_metrics.push(msg);
            }
            Some(msg) = godot_rx.recv() => {
                godot_metrics.push(msg);
            }
            Some(msg) = docker_rx.recv() => {
                docker_metrics.push(msg);
            }
            Some(msg) = system_rx.recv() => {
                system_metrics.push(msg);
            }
            _ = tokio::time::sleep(Duration::from_millis(100)) => {
                // Continue collecting
            }
        }
    }

    // Build export data
    let mut export_data = HashMap::new();
    export_data.insert("streaming", serde_json::to_value(&streaming_metrics)?);
    export_data.insert("godot", serde_json::to_value(&godot_metrics)?);
    export_data.insert("docker", serde_json::to_value(&docker_metrics)?);
    export_data.insert("system", serde_json::to_value(&system_metrics)?);
    export_data.insert("duration_seconds", serde_json::to_value(duration_secs)?);
    export_data.insert("timestamp", serde_json::to_value(chrono::Utc::now().to_rfc3339())?);

    // Write to file
    let json = serde_json::to_string_pretty(&export_data)?;
    fs::write(output, json)?;

    println!();
    println!("{} Exported diagnostics to: {}", style("✓").green(), output);
    println!("  {} streaming metrics", style(streaming_metrics.len()).bold());
    println!("  {} godot metrics", style(godot_metrics.len()).bold());
    println!("  {} docker metrics", style(docker_metrics.len()).bold());
    println!("  {} system metrics", style(system_metrics.len()).bold());

    Ok(())
}
