use crate::app::AppState;
use ratatui::prelude::*;
use ratatui::widgets::{Block, Borders, List, ListItem, Paragraph, Wrap};
use serde_json::Value;

const BRAND_PURPLE: Color = Color::Rgb(138, 43, 226);
const CYBER_CYAN: Color = Color::Rgb(0, 255, 255);
const NEON_GREEN: Color = Color::Rgb(57, 255, 20);
const AMBER_WARN: Color = Color::Rgb(255, 191, 0);
const SHIELD_ORANGE: Color = Color::Rgb(255, 140, 0);
const TEXT_PRIMARY: Color = Color::Rgb(230, 230, 240);
const TEXT_DIM: Color = Color::Rgb(100, 100, 120);
const TEXT_MUTED: Color = Color::Rgb(60, 60, 80);
const BG_PANEL: Color = Color::Rgb(18, 18, 25);

const ABOUT_SHIELD: &[&str] = &[
    "Shield is 4Runr's input/output safety layer on the Gateway run path.",
    "",
    "Production enforcement (every real run):",
    "  • Input check before agent execution starts",
    "  • Injection patterns → block (failed run)",
    "  • PII in prompts → mask or block per policy",
    "  • Output check after agent returns",
    "",
    "This screen shows live Gateway state — no test runs required.",
    "Counters increment as real agent/API traffic is evaluated.",
];

#[derive(Debug, Clone)]
pub struct ShieldBlockRow {
    pub id: String,
    pub name: String,
    pub reason: String,
    pub created_at: String,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ShieldTab {
    Activity,
    Overview,
    Enforcement,
}

impl ShieldTab {
    pub fn label(self) -> &'static str {
        match self {
            ShieldTab::Activity => "Activity",
            ShieldTab::Overview => "Overview",
            ShieldTab::Enforcement => "Enforcement",
        }
    }

    pub fn next(self) -> Self {
        match self {
            ShieldTab::Activity => ShieldTab::Overview,
            ShieldTab::Overview => ShieldTab::Enforcement,
            ShieldTab::Enforcement => ShieldTab::Activity,
        }
    }

    pub fn prev(self) -> Self {
        match self {
            ShieldTab::Activity => ShieldTab::Enforcement,
            ShieldTab::Overview => ShieldTab::Activity,
            ShieldTab::Enforcement => ShieldTab::Overview,
        }
    }
}

#[derive(Debug, Clone)]
pub struct ShieldActivityRow {
    pub at: String,
    pub kind: String,
    pub run_id: Option<String>,
    pub run_name: Option<String>,
    pub detail: String,
    pub source: String,
}

#[derive(Debug, Clone)]
pub struct ShieldDashboardState {
    pub tab: ShieldTab,
    pub selected_block_index: usize,
    pub selected_activity_index: usize,
    pub loaded_once: bool,
    pub snapshot_at: String,
    pub blocks_delta: i64,
    pub masks_delta: i64,
    pub decisions_delta: i64,
    pub breakdown_lines: Vec<String>,
    pub activity_events: Vec<ShieldActivityRow>,
    prev_blocks: u64,
    prev_masks: u64,
    prev_decisions: u64,
    pub enabled: bool,
    pub mode: String,
    pub health_status: String,
    pub pii_enabled: bool,
    pub injection_enabled: bool,
    pub injection_action: String,
    pub hallucination_enabled: bool,
    pub hallucination_action: String,
    pub decisions_total: u64,
    pub blocks_total: u64,
    pub masks_total: u64,
    pub rewrites_total: u64,
    pub warnings: Vec<String>,
    pub recent_blocks: Vec<ShieldBlockRow>,
    pub loading: bool,
    pub loading_started: Option<std::time::Instant>,
    pub error: Option<String>,
    pub last_refresh: Option<std::time::Instant>,
    pub auto_refresh_enabled: bool,
    pub auto_refresh_interval: std::time::Duration,
}

impl Default for ShieldDashboardState {
    fn default() -> Self {
        Self {
            tab: ShieldTab::Activity,
            selected_block_index: 0,
            selected_activity_index: 0,
            loaded_once: false,
            snapshot_at: String::new(),
            blocks_delta: 0,
            masks_delta: 0,
            decisions_delta: 0,
            breakdown_lines: Vec::new(),
            activity_events: Vec::new(),
            prev_blocks: 0,
            prev_masks: 0,
            prev_decisions: 0,
            enabled: false,
            mode: "off".to_string(),
            health_status: String::new(),
            pii_enabled: false,
            injection_enabled: false,
            injection_action: String::new(),
            hallucination_enabled: false,
            hallucination_action: String::new(),
            decisions_total: 0,
            blocks_total: 0,
            masks_total: 0,
            rewrites_total: 0,
            warnings: Vec::new(),
            recent_blocks: Vec::new(),
            loading: false,
            loading_started: None,
            error: None,
            last_refresh: None,
            auto_refresh_enabled: true,
            auto_refresh_interval: std::time::Duration::from_secs(5),
        }
    }
}

impl ShieldDashboardState {

    pub fn apply_load(
        &mut self,
        health: &ShieldHealthSnap,
        config: &ShieldConfigSnap,
        metrics: &ShieldMetricsSnap,
        warnings: Vec<String>,
        recent: Vec<ShieldBlockRow>,
        activity: Vec<ShieldActivityRow>,
        breakdown_lines: Vec<String>,
        snapshot_at: String,
    ) {
        if self.loaded_once {
            self.blocks_delta = metrics.blocks as i64 - self.prev_blocks as i64;
            self.masks_delta = metrics.masks as i64 - self.prev_masks as i64;
            self.decisions_delta = metrics.decisions as i64 - self.prev_decisions as i64;
        } else {
            self.blocks_delta = 0;
            self.masks_delta = 0;
            self.decisions_delta = 0;
        }
        self.prev_blocks = metrics.blocks;
        self.prev_masks = metrics.masks;
        self.prev_decisions = metrics.decisions;

        self.loading = false;
        self.loading_started = None;
        self.error = None;
        self.loaded_once = true;
        self.snapshot_at = snapshot_at;
        self.activity_events = activity;
        self.breakdown_lines = breakdown_lines;
        if self.selected_activity_index >= self.activity_events.len() {
            self.selected_activity_index = 0;
        }
        self.enabled = health.enabled;
        self.mode = health.mode.clone();
        self.health_status = health.status.clone();
        self.pii_enabled = config.pii_enabled;
        self.injection_enabled = config.injection_enabled;
        self.injection_action = config.injection_action.clone();
        self.hallucination_enabled = config.hallucination_enabled;
        self.hallucination_action = config.hallucination_action.clone();
        self.decisions_total = metrics.decisions;
        self.blocks_total = metrics.blocks;
        self.masks_total = metrics.masks;
        self.rewrites_total = metrics.rewrites;
        self.warnings = warnings;
        self.recent_blocks = recent;
        if self.selected_block_index >= self.recent_blocks.len() {
            self.selected_block_index = 0;
        }
        self.last_refresh = Some(std::time::Instant::now());
    }

    pub fn mark_loading(&mut self) {
        self.loading = true;
        if self.loading_started.is_none() {
            self.loading_started = Some(std::time::Instant::now());
        }
    }

    pub fn fail_loading(&mut self, message: String) {
        self.loading = false;
        self.loading_started = None;
        self.error = Some(message);
    }
}

#[derive(Debug, Clone, Default)]
pub struct ShieldHealthSnap {
    pub enabled: bool,
    pub mode: String,
    pub status: String,
}

#[derive(Debug, Clone, Default)]
pub struct ShieldConfigSnap {
    pub pii_enabled: bool,
    pub injection_enabled: bool,
    pub injection_action: String,
    pub hallucination_enabled: bool,
    pub hallucination_action: String,
}

#[derive(Debug, Clone, Default)]
pub struct ShieldMetricsSnap {
    pub decisions: u64,
    pub blocks: u64,
    pub masks: u64,
    pub rewrites: u64,
}

pub fn parse_shield_load(
    health_val: &Value,
    config_val: &Value,
    metrics_val: &Value,
    gateway_health_val: &Value,
    runs_val: &Value,
) -> (
    ShieldHealthSnap,
    ShieldConfigSnap,
    ShieldMetricsSnap,
    Vec<String>,
    Vec<ShieldBlockRow>,
) {
    let health_obj = health_val.as_object();
    let health = ShieldHealthSnap {
        enabled: health_obj
            .and_then(|o| o.get("enabled"))
            .and_then(|v| v.as_bool())
            .unwrap_or(false),
        mode: health_obj
            .and_then(|o| o.get("mode"))
            .and_then(|v| v.as_str())
            .unwrap_or("off")
            .to_string(),
        status: if health_obj
            .and_then(|o| o.get("enabled"))
            .and_then(|v| v.as_bool())
            .unwrap_or(false)
        {
            "active".to_string()
        } else {
            "disabled".to_string()
        },
    };

    let cfg = config_val
        .get("config")
        .or(Some(config_val))
        .and_then(|v| v.as_object());
    let pii = cfg.and_then(|c| c.get("pii")).and_then(|v| v.as_object());
    let injection = cfg.and_then(|c| c.get("injection")).and_then(|v| v.as_object());
    let hallucination = cfg
        .and_then(|c| c.get("hallucination"))
        .and_then(|v| v.as_object());

    let config = ShieldConfigSnap {
        pii_enabled: pii
            .and_then(|o| o.get("enabled"))
            .and_then(|v| v.as_bool())
            .unwrap_or(false),
        injection_enabled: injection
            .and_then(|o| o.get("enabled"))
            .and_then(|v| v.as_bool())
            .unwrap_or(false),
        injection_action: injection
            .and_then(|o| o.get("action"))
            .and_then(|v| v.as_str())
            .unwrap_or("—")
            .to_string(),
        hallucination_enabled: hallucination
            .and_then(|o| o.get("enabled"))
            .and_then(|v| v.as_bool())
            .unwrap_or(false),
        hallucination_action: hallucination
            .and_then(|o| o.get("action"))
            .and_then(|v| v.as_str())
            .unwrap_or("—")
            .to_string(),
    };

    let metrics = ShieldMetricsSnap {
        decisions: metrics_val
            .get("decisions")
            .and_then(|v| v.as_u64())
            .unwrap_or(0),
        blocks: metrics_val
            .get("blocks")
            .and_then(|v| v.as_u64())
            .unwrap_or(0),
        masks: metrics_val
            .get("masks")
            .and_then(|v| v.as_u64())
            .unwrap_or(0),
        rewrites: metrics_val
            .get("rewrites")
            .and_then(|v| v.as_u64())
            .unwrap_or(0),
    };

    let mut warnings: Vec<String> = Vec::new();
    if let Some(sh) = gateway_health_val
        .get("checks")
        .and_then(|c| c.get("shield"))
        .and_then(|v| v.as_object())
    {
        if let Some(st) = sh.get("status").and_then(|v| v.as_str()) {
            if st != "ok" && st != "up" {
                warnings.push(format!("Gateway health: {}", st));
            }
        }
        if let Some(arr) = sh.get("warnings").and_then(|v| v.as_array()) {
            for w in arr {
                if let Some(s) = w.as_str() {
                    warnings.push(s.to_string());
                }
            }
        }
    }

    let mut recent: Vec<ShieldBlockRow> = Vec::new();
    let runs_arr = runs_val
        .as_array()
        .or_else(|| runs_val.get("runs").and_then(|v| v.as_array()));
    if let Some(arr) = runs_arr {
        for item in arr {
            let o = match item.as_object() {
                Some(o) => o,
                None => continue,
            };
            let output = o.get("output").and_then(|v| v.as_object());
            let err = output
                .and_then(|oo| oo.get("error"))
                .and_then(|v| v.as_str())
                .unwrap_or("");
            let logs_shield = o
                .get("logs")
                .and_then(|l| l.as_array())
                .map(|arr| {
                    arr.iter().any(|e| {
                        e.get("message")
                            .and_then(|m| m.as_str())
                            .map(|s| s.to_lowercase().contains("shield"))
                            .unwrap_or(false)
                    })
                })
                .unwrap_or(false);
            if !err.to_lowercase().contains("shield") && !logs_shield {
                continue;
            }
            let id = o
                .get("id")
                .and_then(|v| v.as_str())
                .unwrap_or("?")
                .to_string();
            let name = o
                .get("name")
                .and_then(|v| v.as_str())
                .unwrap_or("Run")
                .to_string();
            let reason = output
                .and_then(|oo| oo.get("reason"))
                .and_then(|v| v.as_str())
                .unwrap_or(err)
                .to_string();
            let created_at = o
                .get("createdAt")
                .and_then(|v| v.as_str())
                .map(|s| s.chars().take(19).collect())
                .unwrap_or_else(|| "—".to_string());
            recent.push(ShieldBlockRow {
                id,
                name,
                reason,
                created_at,
            });
        }
    }
    recent.truncate(12);

    (health, config, metrics, warnings, recent)
}

pub fn parse_shield_activity(val: &Value) -> Vec<ShieldActivityRow> {
    let Some(arr) = val.as_array() else {
        return Vec::new();
    };
    arr.iter()
        .filter_map(|item| {
            let o = item.as_object()?;
            Some(ShieldActivityRow {
                at: o
                    .get("at")
                    .and_then(|v| v.as_str())
                    .map(|s| s.chars().take(19).collect())
                    .unwrap_or_else(|| "—".to_string()),
                kind: o
                    .get("kind")
                    .and_then(|v| v.as_str())
                    .unwrap_or("log")
                    .to_string(),
                run_id: o
                    .get("runId")
                    .and_then(|v| v.as_str())
                    .map(|s| s.to_string()),
                run_name: o
                    .get("runName")
                    .and_then(|v| v.as_str())
                    .map(|s| s.to_string()),
                detail: o
                    .get("detail")
                    .and_then(|v| v.as_str())
                    .unwrap_or("")
                    .to_string(),
                source: o
                    .get("source")
                    .and_then(|v| v.as_str())
                    .unwrap_or("?")
                    .to_string(),
            })
        })
        .collect()
}

pub fn parse_shield_breakdown(val: &Value) -> Vec<String> {
    val.get("breakdownLines")
        .and_then(|v| v.as_array())
        .map(|arr| {
            arr.iter()
                .filter_map(|x| x.as_str().map(|s| s.to_string()))
                .collect()
        })
        .unwrap_or_default()
}

fn format_delta(delta: i64) -> String {
    if delta > 0 {
        format!(" +{}", delta)
    } else if delta < 0 {
        format!(" {}", delta)
    } else {
        String::new()
    }
}

fn refresh_ago(sd: &ShieldDashboardState) -> String {
    sd.last_refresh
        .map(|lr| {
            let s = lr.elapsed().as_secs();
            if s < 60 {
                format!("{}s ago", s)
            } else {
                format!("{}m ago", s / 60)
            }
        })
        .unwrap_or_else(|| "—".to_string())
}

fn kind_color(kind: &str) -> Color {
    match kind {
        "block" => SHIELD_ORANGE,
        "mask" => CYBER_CYAN,
        "flag" => AMBER_WARN,
        "rewrite" => AMBER_WARN,
        _ => TEXT_DIM,
    }
}

pub fn render(f: &mut Frame, state: &AppState) {
    let area = f.size();
    let sd = &state.shield_dashboard;

    use ratatui::layout::{Constraint, Direction, Layout};

    let refresh_hint = if sd.loading {
        " ↻"
    } else {
        ""
    };
    let live = if sd.auto_refresh_enabled { " · LIVE" } else { "" };
    let delta_blk = if sd.loaded_once {
        format_delta(sd.blocks_delta)
    } else {
        String::new()
    };
    let delta_mask = if sd.loaded_once {
        format_delta(sd.masks_delta)
    } else {
        String::new()
    };
    let ago = if sd.loaded_once {
        format!(" · {}", refresh_ago(sd))
    } else {
        String::new()
    };
    let header_title = format!(
        " 🛡️  Shield — {} · {} blk{}{} mask{}{}{}{} ",
        if sd.loaded_once {
            sd.mode.to_uppercase()
        } else {
            "…".to_string()
        },
        sd.blocks_total,
        delta_blk,
        sd.masks_total,
        delta_mask,
        live,
        ago,
        refresh_hint
    );

    let chunks = Layout::default()
        .direction(Direction::Vertical)
        .constraints([
            Constraint::Length(3),
            Constraint::Length(3),
            Constraint::Min(8),
            Constraint::Length(3),
        ])
        .split(area);

    let header = Block::default()
        .title(header_title)
        .borders(Borders::ALL)
        .border_style(Style::default().fg(SHIELD_ORANGE))
        .style(Style::default().bg(BG_PANEL));
    f.render_widget(header, chunks[0]);

    render_tab_bar(f, chunks[1], sd);

    if !sd.loaded_once && sd.loading {
        let block = Block::default()
            .title(" Loading ")
            .borders(Borders::ALL)
            .border_style(Style::default().fg(CYBER_CYAN));
        let inner = block.inner(chunks[2]);
        f.render_widget(block, chunks[2]);
        f.render_widget(
            Paragraph::new(vec![
                Line::from(""),
                Line::from("Fetching Shield health, config, metrics, and run history…")
                    .style(Style::default().fg(TEXT_PRIMARY)),
                Line::from(""),
                Line::from("Press R to retry if this takes more than a few seconds.")
                    .style(Style::default().fg(TEXT_DIM)),
            ])
            .alignment(Alignment::Center),
            inner,
        );
    } else {
        match sd.tab {
            ShieldTab::Activity => render_activity_panel(f, chunks[2], sd),
            ShieldTab::Overview => {
                let mid = Layout::default()
                    .direction(Direction::Horizontal)
                    .constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
                    .split(chunks[2]);
                render_status_panel(f, mid[0], sd);
                render_metrics_panel(f, mid[1], sd);
            }
            ShieldTab::Enforcement => render_enforcement_panel(f, chunks[2], sd),
        }
    }

    let status_line = if let Some(e) = &sd.error {
        format!("⚠ {}  |  ", e)
    } else if sd.loading {
        "Refreshing…  |  ".to_string()
    } else {
        String::new()
    };
    let nav_hint = match sd.tab {
        ShieldTab::Activity => "↑↓ Select event · Enter Open run",
        ShieldTab::Enforcement => "↑↓ Select block · Enter Open run",
        ShieldTab::Overview => "Overview — counters update on refresh",
    };
    let footer_msg = format!(
        "{}Tab/←→ Switch · {} · R Refresh · A Live · ESC Close",
        status_line, nav_hint
    );
    let footer_color = if sd.error.is_some() {
        Color::Rgb(255, 69, 69)
    } else {
        TEXT_DIM
    };
    let footer = Block::default()
        .title(" ⌨️  Actions ")
        .borders(Borders::ALL)
        .border_style(Style::default().fg(TEXT_MUTED));
    let inner = footer.inner(chunks[3]);
    f.render_widget(footer, chunks[3]);
    f.render_widget(
        Paragraph::new(footer_msg)
            .style(Style::default().fg(footer_color))
            .alignment(Alignment::Center),
        inner,
    );
}

fn render_tab_bar(f: &mut Frame, area: Rect, sd: &ShieldDashboardState) {
    let tabs = [ShieldTab::Activity, ShieldTab::Overview, ShieldTab::Enforcement];
    let spans: Vec<Span> = tabs
        .iter()
        .flat_map(|tab| {
            let active = *tab == sd.tab;
            [
                Span::styled(
                    format!(" {} ", tab.label()),
                    Style::default()
                        .fg(if active { SHIELD_ORANGE } else { TEXT_DIM })
                        .add_modifier(if active {
                            Modifier::BOLD
                        } else {
                            Modifier::empty()
                        }),
                ),
                Span::raw("│"),
            ]
        })
        .collect();
    let block = Block::default()
        .borders(Borders::LEFT | Borders::RIGHT)
        .border_style(Style::default().fg(TEXT_MUTED));
    f.render_widget(
        Paragraph::new(Line::from(spans)).alignment(Alignment::Center),
        block.inner(area),
    );
    f.render_widget(block, area);
}

fn render_activity_panel(f: &mut Frame, area: Rect, sd: &ShieldDashboardState) {
    use ratatui::layout::{Constraint, Direction, Layout};

    let outer = Layout::default()
        .direction(Direction::Vertical)
        .constraints([Constraint::Length(3), Constraint::Min(6)])
        .split(area);

    let counter_block = Block::default()
        .title(format!(
            " Live counters (refreshed {}) ",
            refresh_ago(sd)
        ))
        .borders(Borders::ALL)
        .border_style(Style::default().fg(NEON_GREEN));
    let counter_inner = counter_block.inner(outer[0]);
    f.render_widget(counter_block, outer[0]);
    f.render_widget(
        Paragraph::new(Line::from(vec![
            Span::styled("Decisions ", Style::default().fg(TEXT_DIM)),
            Span::styled(
                sd.decisions_total.to_string(),
                Style::default().fg(TEXT_PRIMARY).bold(),
            ),
            Span::styled(
                format_delta(sd.decisions_delta),
                Style::default().fg(if sd.decisions_delta > 0 {
                    NEON_GREEN
                } else {
                    TEXT_MUTED
                }),
            ),
            Span::raw("   "),
            Span::styled("Blocks ", Style::default().fg(TEXT_DIM)),
            Span::styled(
                sd.blocks_total.to_string(),
                Style::default().fg(SHIELD_ORANGE).bold(),
            ),
            Span::styled(
                format_delta(sd.blocks_delta),
                Style::default().fg(if sd.blocks_delta > 0 {
                    NEON_GREEN
                } else {
                    TEXT_MUTED
                }),
            ),
            Span::raw("   "),
            Span::styled("Masks ", Style::default().fg(TEXT_DIM)),
            Span::styled(
                sd.masks_total.to_string(),
                Style::default().fg(CYBER_CYAN).bold(),
            ),
            Span::styled(
                format_delta(sd.masks_delta),
                Style::default().fg(if sd.masks_delta > 0 {
                    NEON_GREEN
                } else {
                    TEXT_MUTED
                }),
            ),
            Span::raw("   "),
            Span::styled("Rewrites ", Style::default().fg(TEXT_DIM)),
            Span::styled(
                sd.rewrites_total.to_string(),
                Style::default().fg(AMBER_WARN).bold(),
            ),
        ])),
        counter_inner,
    );

    let body = Layout::default()
        .direction(Direction::Horizontal)
        .constraints([Constraint::Percentage(62), Constraint::Percentage(38)])
        .split(outer[1]);

    let feed_title = format!(
        " Shield events ({}) — ↑↓ select · Enter open run ",
        sd.activity_events.len()
    );
    let feed_block = Block::default()
        .title(feed_title)
        .borders(Borders::ALL)
        .border_style(Style::default().fg(SHIELD_ORANGE))
        .style(Style::default().bg(BG_PANEL));
    let feed_inner = feed_block.inner(body[0]);
    f.render_widget(feed_block, body[0]);

    if sd.activity_events.is_empty() {
        let empty = vec![
            Line::from(""),
            Line::from("No Shield events in recent Gateway history yet.")
                .style(Style::default().fg(TEXT_DIM)),
            Line::from(""),
            Line::from("Counters above update from Prometheus on every refresh.")
                .style(Style::default().fg(TEXT_MUTED)),
            Line::from("Run an agent with injection/PII to see blocks and masks here.")
                .style(Style::default().fg(TEXT_MUTED)),
        ];
        f.render_widget(Paragraph::new(empty).alignment(Alignment::Center), feed_inner);
    } else {
        let items: Vec<ListItem> = sd
            .activity_events
            .iter()
            .enumerate()
            .map(|(idx, e)| {
                let selected = idx == sd.selected_activity_index;
                let prefix = if selected { "▶ " } else { "  " };
                let kind_upper = e.kind.to_uppercase();
                let run_hint = e
                    .run_name
                    .clone()
                    .or_else(|| {
                        e.run_id.as_ref().map(|id| {
                            if id.len() > 8 {
                                id[id.len() - 8..].to_string()
                            } else {
                                id.clone()
                            }
                        })
                    })
                    .unwrap_or_else(|| "—".to_string());
                ListItem::new(Line::from(vec![
                    Span::styled(prefix, Style::default().fg(SHIELD_ORANGE).bold()),
                    Span::styled(
                        format!("{} ", e.at),
                        Style::default().fg(TEXT_MUTED),
                    ),
                    Span::styled(
                        format!("[{}] ", kind_upper),
                        Style::default().fg(kind_color(&e.kind)).bold(),
                    ),
                    Span::styled(
                        format!("{} ", run_hint),
                        Style::default().fg(if selected { CYBER_CYAN } else { TEXT_PRIMARY }),
                    ),
                    Span::styled(
                        e.detail.chars().take(52).collect::<String>(),
                        Style::default().fg(TEXT_DIM),
                    ),
                    Span::styled(
                        format!(" ({})", e.source),
                        Style::default().fg(TEXT_MUTED),
                    ),
                ]))
            })
            .collect();
        f.render_widget(List::new(items), feed_inner);
    }

    let breakdown_block = Block::default()
        .title(" Prometheus breakdown ")
        .borders(Borders::ALL)
        .border_style(Style::default().fg(BRAND_PURPLE))
        .style(Style::default().bg(BG_PANEL));
    let breakdown_inner = breakdown_block.inner(body[1]);
    f.render_widget(breakdown_block, body[1]);

    let mut breakdown_lines: Vec<Line> = Vec::new();
    if !sd.snapshot_at.is_empty() {
        breakdown_lines.push(Line::from(vec![
            Span::styled("Snapshot: ", Style::default().fg(TEXT_DIM)),
            Span::styled(
                sd.snapshot_at.chars().take(19).collect::<String>(),
                Style::default().fg(TEXT_MUTED),
            ),
        ]));
        breakdown_lines.push(Line::from(""));
    }
    for line in &sd.breakdown_lines {
        let style = if line.ends_with(':') || !line.starts_with(' ') {
            Style::default().fg(NEON_GREEN).bold()
        } else {
            Style::default().fg(TEXT_PRIMARY)
        };
        breakdown_lines.push(Line::from(line.as_str()).style(style));
    }
    if !sd.warnings.is_empty() {
        breakdown_lines.push(Line::from(""));
        breakdown_lines.push(
            Line::from("Warnings:").style(Style::default().fg(AMBER_WARN).bold()),
        );
        for w in &sd.warnings {
            breakdown_lines.push(
                Line::from(format!("  ⚠ {}", w)).style(Style::default().fg(AMBER_WARN)),
            );
        }
    }
    f.render_widget(
        Paragraph::new(breakdown_lines).wrap(Wrap { trim: false }),
        breakdown_inner,
    );
}

fn render_status_panel(f: &mut Frame, area: Rect, sd: &ShieldDashboardState) {
    let block = Block::default()
        .title(" Status & detectors ")
        .borders(Borders::ALL)
        .border_style(Style::default().fg(CYBER_CYAN))
        .style(Style::default().bg(BG_PANEL));
    let inner = block.inner(area);
    f.render_widget(block, area);

    let mode_color = match sd.mode.as_str() {
        "enforce" => NEON_GREEN,
        "monitor" => AMBER_WARN,
        _ => TEXT_MUTED,
    };

    let mut lines = vec![
        Line::from(vec![
            Span::styled("Enabled:  ", Style::default().fg(TEXT_DIM)),
            Span::styled(
                if sd.enabled { "YES" } else { "NO" },
                Style::default()
                    .fg(if sd.enabled { NEON_GREEN } else { TEXT_MUTED })
                    .bold(),
            ),
        ]),
        Line::from(vec![
            Span::styled("Mode:     ", Style::default().fg(TEXT_DIM)),
            Span::styled(
                sd.mode.to_uppercase(),
                Style::default().fg(mode_color).bold(),
            ),
        ]),
        Line::from(""),
        Line::from("Detectors (production path):").style(Style::default().fg(TEXT_DIM)),
        detector_line("PII", sd.pii_enabled, "mask on input/output".to_string()),
        detector_line(
            "Injection",
            sd.injection_enabled,
            format!("action: {}", sd.injection_action),
        ),
        detector_line(
            "Hallucination",
            sd.hallucination_enabled,
            format!("action: {}", sd.hallucination_action),
        ),
    ];

    if !sd.warnings.is_empty() {
        lines.push(Line::from(""));
        lines.push(
            Line::from("Warnings:")
                .style(Style::default().fg(AMBER_WARN).bold()),
        );
        for w in &sd.warnings {
            lines.push(Line::from(format!("  ⚠ {}", w)).style(Style::default().fg(AMBER_WARN)));
        }
    }

    lines.push(Line::from(""));
    lines.push(Line::from(vec![
        Span::styled("Last refresh: ", Style::default().fg(TEXT_DIM)),
        Span::styled(refresh_ago(sd), Style::default().fg(TEXT_MUTED)),
    ]));
    if let Some(first) = sd.activity_events.first() {
        lines.push(Line::from(vec![
            Span::styled("Latest event: ", Style::default().fg(TEXT_DIM)),
            Span::styled(
                format!("[{}] {}", first.kind.to_uppercase(), first.detail.chars().take(40).collect::<String>()),
                Style::default().fg(kind_color(&first.kind)),
            ),
        ]));
    }

    f.render_widget(Paragraph::new(lines).wrap(Wrap { trim: false }), inner);
}

fn detector_line(name: &str, on: bool, detail: String) -> Line<'static> {
    Line::from(vec![
        Span::raw("  "),
        Span::styled(
            if on { "●" } else { "○" },
            Style::default().fg(if on { NEON_GREEN } else { TEXT_MUTED }),
        ),
        Span::styled(format!(" {:<14}", name), Style::default().fg(TEXT_PRIMARY)),
        Span::styled(detail, Style::default().fg(TEXT_DIM)),
    ])
}

fn render_metrics_panel(f: &mut Frame, area: Rect, sd: &ShieldDashboardState) {
    let block = Block::default()
        .title(" Gateway metrics (cumulative) ")
        .borders(Borders::ALL)
        .border_style(Style::default().fg(BRAND_PURPLE))
        .style(Style::default().bg(BG_PANEL));
    let inner = block.inner(area);
    f.render_widget(block, area);

    let lines = vec![
        Line::from(""),
        metric_line_delta("Decisions", sd.decisions_total, sd.decisions_delta, TEXT_PRIMARY),
        metric_line_delta("Blocks", sd.blocks_total, sd.blocks_delta, SHIELD_ORANGE),
        metric_line_delta("Masks", sd.masks_total, sd.masks_delta, CYBER_CYAN),
        metric_line("Rewrites total", sd.rewrites_total, AMBER_WARN),
        Line::from(""),
        Line::from(format!("Refreshed {}", refresh_ago(sd))).style(Style::default().fg(TEXT_MUTED)),
        Line::from("Source: Gateway Prometheus /metrics").style(Style::default().fg(TEXT_MUTED)),
        Line::from("+N = change since last refresh on this screen.").style(Style::default().fg(TEXT_MUTED)),
        Line::from(""),
        Line::from("Activity tab shows live event feed + breakdown.").style(Style::default().fg(NEON_GREEN)),
    ];

    f.render_widget(Paragraph::new(lines), inner);
}

fn metric_line(label: &str, value: u64, color: Color) -> Line<'static> {
    Line::from(vec![
        Span::styled(format!("  {:<18}", label), Style::default().fg(TEXT_DIM)),
        Span::styled(
            value.to_string(),
            Style::default().fg(color).bold(),
        ),
    ])
}

fn metric_line_delta(label: &str, value: u64, delta: i64, color: Color) -> Line<'static> {
    Line::from(vec![
        Span::styled(format!("  {:<18}", label), Style::default().fg(TEXT_DIM)),
        Span::styled(
            value.to_string(),
            Style::default().fg(color).bold(),
        ),
        Span::styled(
            format_delta(delta),
            Style::default().fg(if delta > 0 { NEON_GREEN } else { TEXT_MUTED }),
        ),
    ])
}

fn render_enforcement_panel(f: &mut Frame, area: Rect, sd: &ShieldDashboardState) {
    let block = Block::default()
        .title(format!(
            " Blocked runs ({}) — ↑↓ select · Enter open in Run Manager ",
            sd.recent_blocks.len()
        ))
        .borders(Borders::ALL)
        .border_style(Style::default().fg(SHIELD_ORANGE))
        .style(Style::default().bg(BG_PANEL));
    let inner = block.inner(area);
    f.render_widget(block, area);

    if sd.recent_blocks.is_empty() {
        let empty = vec![
            Line::from(""),
            Line::from("No blocked runs in Gateway history yet.").style(Style::default().fg(TEXT_DIM)),
            Line::from(""),
            Line::from("Shield still evaluates every agent/API run on input.").style(Style::default().fg(TEXT_MUTED)),
            Line::from("Activity tab shows live counters and event feed on refresh.").style(Style::default().fg(NEON_GREEN)),
        ];
        f.render_widget(Paragraph::new(empty).alignment(Alignment::Center), inner);
        return;
    }

    let items: Vec<ListItem> = sd
        .recent_blocks
        .iter()
        .enumerate()
        .map(|(idx, r)| {
            let short_id = if r.id.len() > 8 {
                &r.id[r.id.len() - 8..]
            } else {
                &r.id
            };
            let prefix = if idx == sd.selected_block_index {
                "▶ "
            } else {
                "  "
            };
            ListItem::new(Line::from(vec![
                Span::styled(prefix, Style::default().fg(SHIELD_ORANGE).bold()),
                Span::styled(
                    format!("{} ", r.created_at),
                    Style::default().fg(TEXT_MUTED),
                ),
                Span::styled(
                    format!("[{}] ", short_id),
                    Style::default().fg(CYBER_CYAN),
                ),
                Span::styled(
                    format!("{} — ", r.name),
                    Style::default().fg(if idx == sd.selected_block_index {
                        CYBER_CYAN
                    } else {
                        TEXT_PRIMARY
                    }),
                ),
                Span::styled(
                    r.reason.chars().take(48).collect::<String>(),
                    Style::default().fg(SHIELD_ORANGE),
                ),
            ]))
        })
        .collect();

    f.render_widget(List::new(items), inner);
}
