//! Diagnostics Dashboard TUI with ratatui
//!
//! Provides a 3-panel interface for real-time diagnostics:
//! - Left: Category navigation (System, Redis, Streaming, etc.)
//! - Right: Detailed metrics for selected category
//! - Footer: Controls and help

use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use mecha10_core::health::HealthReport;
use mecha10_core::messages::HealthLevel;
use mecha10_diagnostics::prelude::*;
use ratatui::{
    layout::{Constraint, Direction, Layout, Rect},
    style::{Color, Modifier, Style},
    text::{Line, Span},
    widgets::{Block, Borders, List, ListItem, ListState, Paragraph, Wrap},
    Frame,
};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

/// Diagnostic categories for navigation
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DiagnosticsCategory {
    System,
    Redis,
    Streaming,
    Nodes,
    Godot,
}

impl DiagnosticsCategory {
    fn all() -> Vec<Self> {
        vec![Self::System, Self::Redis, Self::Streaming, Self::Nodes, Self::Godot]
    }

    fn label(&self) -> &str {
        match self {
            Self::System => "System",
            Self::Redis => "Redis",
            Self::Streaming => "Streaming",
            Self::Nodes => "Nodes",
            Self::Godot => "Godot",
        }
    }

    fn icon(&self) -> &str {
        match self {
            Self::System => "💻",
            Self::Redis => "📡",
            Self::Streaming => "🎥",
            Self::Nodes => "🔌",
            Self::Godot => "🎮",
        }
    }
}

/// Internal state for all diagnostic metrics
#[derive(Default)]
struct DiagnosticsStateInner {
    // System metrics
    system: Option<DiagnosticMessage<SystemResourceMetrics>>,

    // Redis metrics
    redis_info: Option<DiagnosticMessage<RedisServerInfoMetrics>>,

    // Streaming metrics
    streaming_pipeline: Option<DiagnosticMessage<StreamingPipelineMetrics>>,
    streaming_encoding: Option<DiagnosticMessage<EncodingMetrics>>,
    streaming_bandwidth: Option<DiagnosticMessage<BandwidthMetrics>>,

    // Node health reports (keyed by node_id)
    nodes: HashMap<String, HealthReport>,

    // Godot simulation
    godot_connection: Option<DiagnosticMessage<GodotConnectionMetrics>>,
    godot_performance: Option<DiagnosticMessage<GodotPerformanceMetrics>>,
}

/// Thread-safe metrics state updated from background subscriptions
#[derive(Clone)]
pub struct DiagnosticsState {
    inner: Arc<Mutex<DiagnosticsStateInner>>,
}

impl DiagnosticsState {
    /// Create a new diagnostics state
    pub fn new() -> Self {
        Self {
            inner: Arc::new(Mutex::new(DiagnosticsStateInner::default())),
        }
    }

    // Update methods for each metric type
    pub fn update_system(&self, msg: DiagnosticMessage<SystemResourceMetrics>) {
        if let Ok(mut inner) = self.inner.lock() {
            inner.system = Some(msg);
        }
    }

    pub fn update_redis_info(&self, msg: DiagnosticMessage<RedisServerInfoMetrics>) {
        if let Ok(mut inner) = self.inner.lock() {
            inner.redis_info = Some(msg);
        }
    }

    pub fn update_streaming_pipeline(&self, msg: DiagnosticMessage<StreamingPipelineMetrics>) {
        if let Ok(mut inner) = self.inner.lock() {
            inner.streaming_pipeline = Some(msg);
        }
    }

    pub fn update_streaming_encoding(&self, msg: DiagnosticMessage<EncodingMetrics>) {
        if let Ok(mut inner) = self.inner.lock() {
            inner.streaming_encoding = Some(msg);
        }
    }

    pub fn update_streaming_bandwidth(&self, msg: DiagnosticMessage<BandwidthMetrics>) {
        if let Ok(mut inner) = self.inner.lock() {
            inner.streaming_bandwidth = Some(msg);
        }
    }

    pub fn update_node_health(&self, report: HealthReport) {
        if let Ok(mut inner) = self.inner.lock() {
            inner.nodes.insert(report.node_id.clone(), report);
        }
    }

    pub fn update_godot_connection(&self, msg: DiagnosticMessage<GodotConnectionMetrics>) {
        if let Ok(mut inner) = self.inner.lock() {
            inner.godot_connection = Some(msg);
        }
    }

    pub fn update_godot_performance(&self, msg: DiagnosticMessage<GodotPerformanceMetrics>) {
        if let Ok(mut inner) = self.inner.lock() {
            inner.godot_performance = Some(msg);
        }
    }
}

impl Default for DiagnosticsState {
    fn default() -> Self {
        Self::new()
    }
}

/// TUI for diagnostics dashboard
pub struct DiagnosticsTui {
    state: DiagnosticsState,
    categories: Vec<DiagnosticsCategory>,
    selected_index: usize,
    detail_scroll_offset: usize,
    should_quit: bool,
}

impl DiagnosticsTui {
    /// Create a new diagnostics TUI
    pub fn new(state: DiagnosticsState) -> Self {
        Self {
            state,
            categories: DiagnosticsCategory::all(),
            selected_index: 0,
            detail_scroll_offset: 0,
            should_quit: false,
        }
    }

    /// Check if should quit
    pub fn should_quit(&self) -> bool {
        self.should_quit
    }

    /// Get the currently selected category
    fn selected_category(&self) -> DiagnosticsCategory {
        self.categories[self.selected_index]
    }

    /// Draw the TUI (single frame)
    pub fn draw(&mut self, f: &mut Frame) {
        // Split terminal vertically: content area + footer
        let vertical_chunks = Layout::default()
            .direction(Direction::Vertical)
            .constraints([
                Constraint::Min(10),   // Main content area
                Constraint::Length(3), // Footer
            ])
            .split(f.area());

        // Split main content horizontally: sidebar + details
        let horizontal_chunks = Layout::default()
            .direction(Direction::Horizontal)
            .constraints([
                Constraint::Length(28), // Category sidebar (fixed width for "Control Plane Redis")
                Constraint::Min(40),    // Details panel
            ])
            .split(vertical_chunks[0]);

        self.draw_sidebar(f, horizontal_chunks[0]);
        self.draw_details(f, horizontal_chunks[1]);
        self.draw_footer(f, vertical_chunks[1]);
    }

    /// Draw category sidebar
    fn draw_sidebar(&mut self, f: &mut Frame, area: Rect) {
        let items: Vec<ListItem> = self
            .categories
            .iter()
            .enumerate()
            .map(|(i, cat)| {
                let is_selected = i == self.selected_index;
                let style = if is_selected {
                    Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD)
                } else {
                    Style::default().fg(Color::White)
                };

                let prefix = if is_selected { "▶ " } else { "  " };
                let formatted = format!("{}{} {}", prefix, cat.icon(), cat.label());
                ListItem::new(formatted).style(style)
            })
            .collect();

        let list = List::new(items).block(
            Block::default()
                .borders(Borders::ALL)
                .title(" Categories ")
                .border_style(Style::default().fg(Color::Cyan)),
        );

        let mut state = ListState::default();
        state.select(Some(self.selected_index));

        f.render_stateful_widget(list, area, &mut state);
    }

    /// Draw details panel for selected category
    fn draw_details(&self, f: &mut Frame, area: Rect) {
        match self.selected_category() {
            DiagnosticsCategory::System => self.draw_system_details(f, area),
            DiagnosticsCategory::Redis => self.draw_redis_details(f, area),
            DiagnosticsCategory::Streaming => self.draw_streaming_details(f, area),
            DiagnosticsCategory::Nodes => self.draw_nodes_details(f, area),
            DiagnosticsCategory::Godot => self.draw_godot_details(f, area),
        }
    }

    /// Draw system resource details
    fn draw_system_details(&self, f: &mut Frame, area: Rect) {
        let inner = self.state.inner.lock().unwrap();
        let mut lines = vec![
            Line::from(vec![Span::styled(
                "💻 SYSTEM RESOURCES",
                Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD),
            )]),
            Line::from(""),
        ];

        if let Some(msg) = &inner.system {
            let m = &msg.payload;

            // CPU
            let cpu_bar = self.make_bar(m.cpu_percent, 100.0, 20);
            lines.push(Line::from(vec![
                Span::raw("CPU:     "),
                Span::styled(cpu_bar, self.percent_color(m.cpu_percent)),
                Span::raw(format!("  {:.1}%", m.cpu_percent)),
            ]));

            // Memory
            let mem_bar = self.make_bar(m.memory_percent, 100.0, 20);
            let mem_used_gb = m.memory_used_bytes as f64 / 1_073_741_824.0;
            let mem_total_gb = m.memory_total_bytes as f64 / 1_073_741_824.0;
            lines.push(Line::from(vec![
                Span::raw("Memory:  "),
                Span::styled(mem_bar, self.percent_color(m.memory_percent)),
                Span::raw(format!(
                    "  {:.1}% ({:.1} / {:.1} GB)",
                    m.memory_percent, mem_used_gb, mem_total_gb
                )),
            ]));

            // Disk
            let disk_bar = self.make_bar(m.disk_percent, 100.0, 20);
            let disk_used_gb = m.disk_used_bytes as f64 / 1_073_741_824.0;
            let disk_total_gb = m.disk_total_bytes as f64 / 1_073_741_824.0;
            lines.push(Line::from(vec![
                Span::raw("Disk:    "),
                Span::styled(disk_bar, self.percent_color(m.disk_percent)),
                Span::raw(format!(
                    "  {:.1}% ({:.0} / {:.0} GB)",
                    m.disk_percent, disk_used_gb, disk_total_gb
                )),
            ]));

            // Network
            lines.push(Line::from(""));
            lines.push(Line::from(vec![Span::styled(
                "Network:",
                Style::default().add_modifier(Modifier::BOLD),
            )]));
            let rx_mbps = m.network_rx_bytes_per_sec as f64 / 1_048_576.0;
            let tx_mbps = m.network_tx_bytes_per_sec as f64 / 1_048_576.0;
            lines.push(Line::from(format!("  RX: {:.2} MB/s", rx_mbps)));
            lines.push(Line::from(format!("  TX: {:.2} MB/s", tx_mbps)));

            // Per-core CPU
            if !m.cpu_per_core.is_empty() {
                lines.push(Line::from(""));
                lines.push(Line::from(vec![Span::styled(
                    "Per-Core CPU:",
                    Style::default().add_modifier(Modifier::BOLD),
                )]));
                for (i, cpu) in m.cpu_per_core.iter().enumerate() {
                    let bar = self.make_bar(*cpu, 100.0, 10);
                    lines.push(Line::from(vec![
                        Span::raw(format!("  Core {}: ", i)),
                        Span::styled(bar, self.percent_color(*cpu)),
                        Span::raw(format!(" {:.1}%", cpu)),
                    ]));
                }
            }
        } else {
            lines.push(Line::from(vec![Span::styled(
                "Waiting for metrics...",
                Style::default().fg(Color::DarkGray),
            )]));
        }

        let paragraph = Paragraph::new(lines)
            .block(
                Block::default()
                    .borders(Borders::ALL)
                    .title(" Details ")
                    .border_style(Style::default().fg(Color::Yellow)),
            )
            .wrap(Wrap { trim: false })
            .scroll((self.detail_scroll_offset as u16, 0));

        f.render_widget(paragraph, area);
    }

    /// Draw Redis details
    fn draw_redis_details(&self, f: &mut Frame, area: Rect) {
        let inner = self.state.inner.lock().unwrap();
        let mut lines = vec![
            Line::from(vec![Span::styled(
                "📡 REDIS",
                Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD),
            )]),
            Line::from(""),
        ];

        if let Some(msg) = &inner.redis_info {
            let m = &msg.payload;

            // Connection status
            lines.push(Line::from(vec![
                Span::raw("Status:   "),
                Span::styled("🟢 Connected", Style::default().fg(Color::Green)),
            ]));
            lines.push(Line::from(format!("Version:  {}", m.redis_version)));
            lines.push(Line::from(format!(
                "Uptime:   {}",
                self.format_duration(m.uptime_seconds)
            )));
            lines.push(Line::from(format!("Clients:  {}", m.connected_clients)));

            // Memory
            lines.push(Line::from(""));
            lines.push(Line::from(vec![Span::styled(
                "Memory:",
                Style::default().add_modifier(Modifier::BOLD),
            )]));
            let used_mb = m.used_memory as f64 / 1_048_576.0;
            let peak_mb = m.used_memory_peak as f64 / 1_048_576.0;
            lines.push(Line::from(format!("  Used:  {:.1} MB", used_mb)));
            lines.push(Line::from(format!("  Peak:  {:.1} MB", peak_mb)));
            lines.push(Line::from(format!("  Keys:  {}", m.db0_keys)));

            // Operations
            lines.push(Line::from(""));
            lines.push(Line::from(vec![Span::styled(
                "Operations:",
                Style::default().add_modifier(Modifier::BOLD),
            )]));
            lines.push(Line::from(format!("  Ops/sec:    {}", m.instantaneous_ops_per_sec)));
            lines.push(Line::from(format!("  Total cmds: {}", m.total_commands_processed)));

            // Hit rate
            let total_hits = m.keyspace_hits + m.keyspace_misses;
            if total_hits > 0 {
                let hit_rate = (m.keyspace_hits as f64 / total_hits as f64) * 100.0;
                lines.push(Line::from(format!("  Hit rate:   {:.1}%", hit_rate)));
            }
        } else {
            lines.push(Line::from(vec![Span::styled(
                "Waiting for metrics...",
                Style::default().fg(Color::DarkGray),
            )]));
        }

        let paragraph = Paragraph::new(lines)
            .block(
                Block::default()
                    .borders(Borders::ALL)
                    .title(" Details ")
                    .border_style(Style::default().fg(Color::Yellow)),
            )
            .wrap(Wrap { trim: false })
            .scroll((self.detail_scroll_offset as u16, 0));

        f.render_widget(paragraph, area);
    }

    /// Draw streaming pipeline details
    fn draw_streaming_details(&self, f: &mut Frame, area: Rect) {
        let inner = self.state.inner.lock().unwrap();
        let mut lines = vec![
            Line::from(vec![Span::styled(
                "🎥 STREAMING PIPELINE",
                Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD),
            )]),
            Line::from(""),
        ];

        if let Some(msg) = &inner.streaming_pipeline {
            let m = &msg.payload;

            lines.push(Line::from(format!("FPS:              {:.1}", m.fps)));
            lines.push(Line::from(format!("Frames Received:  {}", m.frames_received)));
            lines.push(Line::from(format!("Frames Encoded:   {}", m.frames_encoded)));
            lines.push(Line::from(format!("Frames Sent:      {}", m.frames_sent)));

            let drop_style = if m.frames_dropped > 0 {
                Style::default().fg(Color::Yellow)
            } else {
                Style::default().fg(Color::Green)
            };
            lines.push(Line::from(vec![
                Span::raw("Frames Dropped:   "),
                Span::styled(format!("{}", m.frames_dropped), drop_style),
                if m.frames_dropped > 0 {
                    Span::styled(" ⚠", Style::default().fg(Color::Yellow))
                } else {
                    Span::styled(" ✓", Style::default().fg(Color::Green))
                },
            ]));

            let bandwidth_kbps = m.bytes_per_second as f64 / 1024.0;
            lines.push(Line::from(format!("Bandwidth:        {:.1} Kbps", bandwidth_kbps)));
        } else {
            lines.push(Line::from(vec![Span::styled(
                "No streaming data",
                Style::default().fg(Color::DarkGray),
            )]));
        }

        // Bandwidth metrics
        if let Some(msg) = &inner.streaming_bandwidth {
            let m = &msg.payload;
            lines.push(Line::from(""));
            lines.push(Line::from(vec![Span::styled(
                "Bandwidth Details:",
                Style::default().add_modifier(Modifier::BOLD),
            )]));
            let bitrate_mbps = m.bitrate_bps as f64 / 1_000_000.0;
            let target_mbps = m.target_bitrate_bps as f64 / 1_000_000.0;
            lines.push(Line::from(format!("  Bitrate:      {:.2} Mbps", bitrate_mbps)));
            lines.push(Line::from(format!("  Target:       {:.2} Mbps", target_mbps)));
            lines.push(Line::from(format!("  Utilization:  {:.1}%", m.utilization * 100.0)));
            lines.push(Line::from(format!("  Avg Frame:    {} bytes", m.avg_frame_size_bytes)));
        }

        // Encoding metrics
        if let Some(msg) = &inner.streaming_encoding {
            let m = &msg.payload;
            lines.push(Line::from(""));
            lines.push(Line::from(vec![Span::styled(
                "Encoding:",
                Style::default().add_modifier(Modifier::BOLD),
            )]));
            lines.push(Line::from(format!("  Queue Depth:  {}", m.queue_depth)));
            lines.push(Line::from(format!("  Slow Frames:  {}", m.slow_frames)));
        }

        let paragraph = Paragraph::new(lines)
            .block(
                Block::default()
                    .borders(Borders::ALL)
                    .title(" Details ")
                    .border_style(Style::default().fg(Color::Yellow)),
            )
            .wrap(Wrap { trim: false })
            .scroll((self.detail_scroll_offset as u16, 0));

        f.render_widget(paragraph, area);
    }

    /// Draw nodes list
    fn draw_nodes_details(&self, f: &mut Frame, area: Rect) {
        let inner = self.state.inner.lock().unwrap();
        let mut lines = vec![
            Line::from(vec![Span::styled(
                "🔌 NODES",
                Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD),
            )]),
            Line::from(""),
        ];

        if inner.nodes.is_empty() {
            lines.push(Line::from(vec![Span::styled(
                "No nodes reporting health",
                Style::default().fg(Color::DarkGray),
            )]));
            lines.push(Line::from(""));
            lines.push(Line::from("Nodes report to /system/health topic."));
            lines.push(Line::from("Start nodes with: mecha10 dev"));
        } else {
            // Sort nodes by name
            let mut nodes: Vec<_> = inner.nodes.values().collect();
            nodes.sort_by(|a, b| a.node_id.cmp(&b.node_id));

            // Current time for calculating age
            let now_us = std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .map(|d| d.as_micros() as u64)
                .unwrap_or(0);

            for report in nodes {
                let (icon, status_style, status_text) = match report.status.level {
                    HealthLevel::Ok => ("🟢", Style::default().fg(Color::Green), "Ok"),
                    HealthLevel::Degraded => ("🟡", Style::default().fg(Color::Yellow), "Degraded"),
                    HealthLevel::Error => ("🔴", Style::default().fg(Color::Red), "Error"),
                    HealthLevel::Unknown => ("⚪", Style::default().fg(Color::DarkGray), "Unknown"),
                };

                // Calculate how long ago the report was
                let age_secs = if report.reported_at > 0 && now_us > report.reported_at {
                    (now_us - report.reported_at) / 1_000_000
                } else {
                    0
                };
                let age_str = if age_secs < 60 {
                    format!("{}s ago", age_secs)
                } else {
                    format!("{}m ago", age_secs / 60)
                };

                lines.push(Line::from(vec![
                    Span::raw(format!("{} ", icon)),
                    Span::styled(&report.node_id, Style::default().add_modifier(Modifier::BOLD)),
                ]));

                lines.push(Line::from(vec![
                    Span::raw("   Status: "),
                    Span::styled(status_text, status_style),
                    Span::raw(format!("  Priority: {:?}", report.priority)),
                ]));

                lines.push(Line::from(format!("   Last report: {}", age_str)));

                // Show message if present
                if !report.status.message.is_empty() {
                    lines.push(Line::from(vec![
                        Span::raw("   └ "),
                        Span::styled(&report.status.message, Style::default().fg(Color::DarkGray)),
                    ]));
                }

                lines.push(Line::from(""));
            }
        }

        let paragraph = Paragraph::new(lines)
            .block(
                Block::default()
                    .borders(Borders::ALL)
                    .title(" Details ")
                    .border_style(Style::default().fg(Color::Yellow)),
            )
            .wrap(Wrap { trim: false })
            .scroll((self.detail_scroll_offset as u16, 0));

        f.render_widget(paragraph, area);
    }

    /// Draw Godot simulation details
    fn draw_godot_details(&self, f: &mut Frame, area: Rect) {
        let inner = self.state.inner.lock().unwrap();
        let mut lines = vec![
            Line::from(vec![Span::styled(
                "🎮 GODOT SIMULATION",
                Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD),
            )]),
            Line::from(""),
        ];

        if let Some(msg) = &inner.godot_connection {
            let m = &msg.payload;

            // Control connection
            let (ctrl_icon, ctrl_style) = if m.control_connected {
                ("🟢", Style::default().fg(Color::Green))
            } else {
                ("🔴", Style::default().fg(Color::Red))
            };
            let ctrl_status = if m.control_connected {
                "Connected"
            } else {
                "Disconnected"
            };
            lines.push(Line::from(vec![
                Span::raw("Control:  "),
                Span::raw(format!("{} ", ctrl_icon)),
                Span::styled(ctrl_status, ctrl_style),
            ]));
            lines.push(Line::from(format!(
                "  Uptime: {}  Reconnects: {}",
                self.format_duration(m.control_uptime_seconds),
                m.control_reconnects
            )));

            // Camera connection
            let (cam_icon, cam_style) = if m.camera_connected {
                ("🟢", Style::default().fg(Color::Green))
            } else {
                ("🔴", Style::default().fg(Color::Red))
            };
            let cam_status = if m.camera_connected {
                "Connected"
            } else {
                "Disconnected"
            };
            lines.push(Line::from(vec![
                Span::raw("Camera:   "),
                Span::raw(format!("{} ", cam_icon)),
                Span::styled(cam_status, cam_style),
            ]));
            lines.push(Line::from(format!(
                "  Uptime: {}  Reconnects: {}",
                self.format_duration(m.camera_uptime_seconds),
                m.camera_reconnects
            )));
        } else {
            lines.push(Line::from(vec![Span::styled(
                "No connection data",
                Style::default().fg(Color::DarkGray),
            )]));
        }

        // Performance metrics
        if let Some(msg) = &inner.godot_performance {
            let m = &msg.payload;
            lines.push(Line::from(""));
            lines.push(Line::from(vec![Span::styled(
                "Performance:",
                Style::default().add_modifier(Modifier::BOLD),
            )]));

            let fps_style = if m.fps < m.target_fps * 0.9 {
                Style::default().fg(Color::Yellow)
            } else {
                Style::default().fg(Color::Green)
            };
            lines.push(Line::from(vec![
                Span::raw("  FPS:         "),
                Span::styled(format!("{:.1}", m.fps), fps_style),
                Span::raw(format!(" / {:.0} target", m.target_fps)),
            ]));

            lines.push(Line::from(format!("  Frame Time:  {:.2} ms", m.frame_time_ms)));
            lines.push(Line::from(format!("  Physics:     {:.2} ms", m.physics_time_ms)));
            lines.push(Line::from(format!("  Render:      {:.2} ms", m.render_time_ms)));

            if m.dropped_frames > 0 {
                lines.push(Line::from(vec![
                    Span::raw("  Dropped:     "),
                    Span::styled(format!("{}", m.dropped_frames), Style::default().fg(Color::Yellow)),
                ]));
            }
        }

        let paragraph = Paragraph::new(lines)
            .block(
                Block::default()
                    .borders(Borders::ALL)
                    .title(" Details ")
                    .border_style(Style::default().fg(Color::Yellow)),
            )
            .wrap(Wrap { trim: false })
            .scroll((self.detail_scroll_offset as u16, 0));

        f.render_widget(paragraph, area);
    }

    /// Draw footer with controls
    fn draw_footer(&self, f: &mut Frame, area: Rect) {
        let footer_text = vec![Line::from(vec![
            Span::raw("  "),
            Span::styled("↑/↓:", Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD)),
            Span::raw(" Navigate  "),
            Span::styled("j/k:", Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD)),
            Span::raw(" Scroll  "),
            Span::styled("q/Esc:", Style::default().fg(Color::Red).add_modifier(Modifier::BOLD)),
            Span::raw(" Exit"),
        ])];

        let footer = Paragraph::new(footer_text).block(
            Block::default()
                .borders(Borders::ALL)
                .border_style(Style::default().fg(Color::DarkGray)),
        );

        f.render_widget(footer, area);
    }

    /// Handle keyboard input
    pub fn handle_key(&mut self, key: KeyEvent) {
        match key.code {
            // Exit
            KeyCode::Char('q') | KeyCode::Esc => {
                self.should_quit = true;
            }
            KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
                self.should_quit = true;
            }

            // Navigate categories
            KeyCode::Up => {
                if self.selected_index > 0 {
                    self.selected_index -= 1;
                    self.detail_scroll_offset = 0;
                }
            }
            KeyCode::Down => {
                if self.selected_index < self.categories.len().saturating_sub(1) {
                    self.selected_index += 1;
                    self.detail_scroll_offset = 0;
                }
            }

            // Scroll details
            KeyCode::Char('k') => {
                self.detail_scroll_offset = self.detail_scroll_offset.saturating_sub(1);
            }
            KeyCode::Char('j') => {
                self.detail_scroll_offset += 1;
            }
            KeyCode::PageUp => {
                self.detail_scroll_offset = self.detail_scroll_offset.saturating_sub(10);
            }
            KeyCode::PageDown => {
                self.detail_scroll_offset += 10;
            }

            _ => {}
        }
    }

    // Helper methods

    /// Create a progress bar string
    fn make_bar(&self, value: f64, max: f64, width: usize) -> String {
        let ratio = (value / max).clamp(0.0, 1.0);
        let filled = (ratio * width as f64).round() as usize;
        let empty = width.saturating_sub(filled);
        format!("{}{}", "█".repeat(filled), "░".repeat(empty))
    }

    /// Get color based on percentage (green < 70, yellow < 90, red >= 90)
    fn percent_color(&self, percent: f64) -> Style {
        if percent >= 90.0 {
            Style::default().fg(Color::Red)
        } else if percent >= 70.0 {
            Style::default().fg(Color::Yellow)
        } else {
            Style::default().fg(Color::Green)
        }
    }

    /// Format duration in human-readable form
    fn format_duration(&self, seconds: u64) -> String {
        if seconds < 60 {
            format!("{}s", seconds)
        } else if seconds < 3600 {
            format!("{}m {}s", seconds / 60, seconds % 60)
        } else if seconds < 86400 {
            let hours = seconds / 3600;
            let mins = (seconds % 3600) / 60;
            format!("{}h {}m", hours, mins)
        } else {
            let days = seconds / 86400;
            let hours = (seconds % 86400) / 3600;
            format!("{}d {}h", days, hours)
        }
    }
}
