#![allow(clippy::indexing_slicing)] // ratatui Layout::split indices match constraints
//! Services cockpit -- service health grid, cluster events, resources.

use ratatui::Frame;
use ratatui::layout::{Constraint, Direction, Layout, Rect};
use ratatui::style::Style;
use ratatui::text::{Line, Span};
use ratatui::widgets::{Gauge, List, ListItem, Row, Table};

use crate::tui::cockpit::Cockpit;
use crate::tui::state::AppState;
use crate::tui::theme;

pub struct ServicesCockpit;

impl Cockpit for ServicesCockpit {
    fn name(&self) -> &str {
        "Services"
    }

    fn render(&self, frame: &mut Frame, area: Rect, state: &AppState) {
        let cols = Layout::default()
            .direction(Direction::Horizontal)
            .constraints([Constraint::Percentage(55), Constraint::Percentage(45)])
            .split(area);

        render_service_grid(frame, cols[0], state);

        let right = Layout::default()
            .direction(Direction::Vertical)
            .constraints([Constraint::Percentage(55), Constraint::Percentage(45)])
            .split(cols[1]);

        render_cluster_events(frame, right[0], state);
        render_cluster_resources(frame, right[1], state);
    }
}

fn render_service_grid(frame: &mut Frame, area: Rect, state: &AppState) {
    let header = Row::new(vec!["Service", "Status", "Latency", "Uptime", "Version"])
        .style(theme::header_style());

    let rows: Vec<Row> = state
        .services
        .iter()
        .map(|s| {
            let status_text = if s.healthy {
                "HEALTHY".to_owned()
            } else {
                match &s.error {
                    Some(msg) if !msg.is_empty() => {
                        let truncated: String = msg.chars().take(20).collect();
                        format!("DOWN: {truncated}")
                    }
                    _ => "DOWN".to_owned(),
                }
            };
            let status_style = theme::status_style(s.healthy);

            let latency_style = if s.latency_ms > 500.0 {
                Style::default().fg(theme::ERROR)
            } else if s.latency_ms > 100.0 {
                Style::default().fg(theme::WARNING)
            } else {
                Style::default().fg(theme::SUCCESS)
            };

            let latency_text = if s.latency_ms > 0.0 {
                format!("{:.0}ms", s.latency_ms)
            } else {
                "\u{2014}".to_owned()
            };

            Row::new(vec![
                Span::styled(s.name.clone(), Style::default().fg(theme::TEXT)),
                Span::styled(status_text, status_style),
                Span::styled(latency_text, latency_style),
                Span::styled(s.uptime.clone(), theme::muted_style()),
                Span::styled(s.version.clone(), theme::muted_style()),
            ])
        })
        .collect();

    let table = Table::new(
        rows,
        [
            Constraint::Length(20),
            Constraint::Length(28),
            Constraint::Length(10),
            Constraint::Length(10),
            Constraint::Length(10),
        ],
    )
    .header(header)
    .block(theme::block(" Service Health "));

    frame.render_widget(table, area);
}

fn render_cluster_events(frame: &mut Frame, area: Rect, state: &AppState) {
    if state.alerts.is_empty() {
        let items = vec![ListItem::new(Line::from(Span::styled(
            "No system alerts",
            theme::muted_style(),
        )))];
        let list = List::new(items).block(theme::block(" System Alerts "));
        frame.render_widget(list, area);
        return;
    }

    let items: Vec<ListItem> = state
        .alerts
        .iter()
        .rev()
        .take(12)
        .map(|a| {
            let sev_style = match a.severity.as_str() {
                "EMRG" | "CRIT" => Style::default().fg(theme::ERROR),
                "WARN" => theme::warning_style(),
                _ => Style::default().fg(theme::SUCCESS),
            };

            ListItem::new(Line::from(vec![
                Span::styled(format!("{} ", a.time), theme::muted_style()),
                Span::styled(format!("[{}] ", a.severity), sev_style),
                Span::styled(format!("{}: ", a.service), Style::default().fg(theme::HIGHLIGHT)),
                Span::styled(&a.title, Style::default().fg(theme::TEXT)),
            ]))
        })
        .collect();

    let list = List::new(items).block(theme::block(" System Alerts "));
    frame.render_widget(list, area);
}

fn render_cluster_resources(frame: &mut Frame, area: Rect, state: &AppState) {
    let block = theme::block(" Cluster Resources ");
    let inner = block.inner(area);
    frame.render_widget(block, area);

    let chunks = Layout::default()
        .direction(Direction::Vertical)
        .constraints([
            Constraint::Length(2),
            Constraint::Length(2),
            Constraint::Length(2),
            Constraint::Min(0),
        ])
        .split(inner);

    // CPU
    let cpu_pct = state.system_resources.cpu_usage_pct / 100.0;
    let cpu_gauge = Gauge::default()
        .gauge_style(Style::default().fg(theme::SECONDARY))
        .label(format!("CPU  {:.1}%", state.system_resources.cpu_usage_pct))
        .ratio(cpu_pct.clamp(0.0, 1.0));
    frame.render_widget(cpu_gauge, chunks[0]);

    // RAM
    let ram_pct = if state.system_resources.ram_total_gb > 0.0 {
        state.system_resources.ram_used_gb / state.system_resources.ram_total_gb
    } else {
        0.0
    };
    let ram_gauge = Gauge::default()
        .gauge_style(Style::default().fg(theme::PRIMARY))
        .label(format!(
            "RAM  {:.1}/{:.0} GB",
            state.system_resources.ram_used_gb, state.system_resources.ram_total_gb,
        ))
        .ratio(ram_pct.clamp(0.0, 1.0));
    frame.render_widget(ram_gauge, chunks[1]);

    // GPU cluster
    let gpu_pct = state.system_resources.gpu_cluster_utilization / 100.0;
    let gpu_gauge = Gauge::default()
        .gauge_style(Style::default().fg(theme::HIGHLIGHT))
        .label(format!(
            "GPU  {:.1}%",
            state.system_resources.gpu_cluster_utilization,
        ))
        .ratio(gpu_pct.clamp(0.0, 1.0));
    frame.render_widget(gpu_gauge, chunks[2]);
}
