use ratatui::prelude::*;
use ratatui::widgets::{Block, Borders, BorderType, Clear, List, ListItem, ListState, Padding, Paragraph, Wrap};

use super::app::{App, Filter};

const GREEN: Color = Color::Green;
const YELLOW: Color = Color::Yellow;
const RED: Color = Color::Red;
const CYAN: Color = Color::Cyan;
const DIM: Color = Color::DarkGray;
const WHITE: Color = Color::White;

pub fn draw(f: &mut Frame, app: &mut App) {
    let area = f.area();

    // Main layout: header + feed + status bar
    let chunks = Layout::default()
        .direction(Direction::Vertical)
        .constraints([
            Constraint::Length(1), // header
            Constraint::Min(5),   // feed
            Constraint::Length(1), // status bar
        ])
        .split(area);

    draw_header(f, app, chunks[0]);
    draw_feed(f, app, chunks[1]);
    draw_status_bar(f, app, chunks[2]);

    if app.show_help {
        draw_help_overlay(f, area);
    }
}

fn draw_header(f: &mut Frame, app: &App, area: Rect) {
    let session_short = if app.session_id.len() > 8 {
        &app.session_id[..8]
    } else {
        &app.session_id
    };

    let elapsed = app.start_time.elapsed();
    let mins = elapsed.as_secs() / 60;
    let secs = elapsed.as_secs() % 60;

    let filter_text = match app.filter {
        Filter::All => "",
        Filter::Blocks => " [blocks]",
        Filter::Approvals => " [approvals]",
    };

    let header = Line::from(vec![
        Span::styled(" railyard", Style::default().fg(WHITE).bold()),
        Span::styled(
            format!(" \u{2500}\u{2500} session {} \u{2500}\u{2500} {} calls \u{2500}\u{2500} {}m {:02}s{}",
                session_short,
                app.entries.len(),
                mins,
                secs,
                filter_text,
            ),
            Style::default().fg(DIM),
        ),
    ]);

    f.render_widget(Paragraph::new(header), area);
}

fn draw_feed(f: &mut Frame, app: &mut App, area: Rect) {
    let feed_block = Block::default()
        .borders(Borders::TOP | Borders::BOTTOM)
        .border_type(BorderType::Rounded)
        .border_style(Style::default().fg(DIM));

    let inner = feed_block.inner(area);
    f.render_widget(feed_block, area);

    if app.filtered_indices.is_empty() {
        let empty = Paragraph::new(Span::styled(
            "  Waiting for tool calls...",
            Style::default().fg(DIM),
        ));
        f.render_widget(empty, inner);
        return;
    }

    // Build list items
    let mut items: Vec<ListItem> = Vec::new();

    for (display_idx, &entry_idx) in app.filtered_indices.iter().enumerate() {
        let entry = &app.entries[entry_idx];

        let (icon, icon_color) = match entry.decision.as_str() {
            "allow" | "completed" => ("\u{2713}", GREEN),    // checkmark
            "approve" | "warn" => ("\u{25cf}", YELLOW),       // filled circle
            "block" => ("\u{2717}", RED),                     // x mark
            _ => ("?", DIM),
        };

        let decision_label = match entry.decision.as_str() {
            "allow" | "completed" => "allow  ",
            "approve" => "approve",
            "warn" => "warn   ",
            "block" => "block  ",
            _ => "???    ",
        };

        let tool_display = format!("{:<6}", if entry.tool.len() > 6 {
            &entry.tool[..6]
        } else {
            &entry.tool
        });

        // Truncate input summary to fit
        let max_input_len = inner.width as usize - 40;
        let input = if entry.input_summary.len() > max_input_len {
            format!("{}...", &entry.input_summary[..max_input_len.saturating_sub(3)])
        } else {
            entry.input_summary.clone()
        };

        // Extract time portion from timestamp (HH:MM:SS)
        let time = extract_time(&entry.timestamp);

        let rule_suffix = if entry.decision == "block" {
            entry.rule.as_deref().map(|r| format!(" \u{2500} {}", r)).unwrap_or_default()
        } else {
            String::new()
        };

        let is_selected = display_idx == app.selected;

        let line = Line::from(vec![
            Span::styled(format!("  {} ", icon), Style::default().fg(icon_color).bold()),
            Span::styled(
                format!("{} ", decision_label),
                Style::default().fg(icon_color),
            ),
            Span::styled(format!("{} ", tool_display), Style::default().fg(CYAN)),
            Span::styled(input, Style::default().fg(WHITE)),
            Span::styled(rule_suffix, Style::default().fg(DIM)),
            Span::styled(format!("  {}", time), Style::default().fg(DIM)),
        ]);

        let style = if is_selected {
            Style::default().bg(Color::Rgb(30, 30, 40))
        } else {
            Style::default()
        };

        items.push(ListItem::new(line).style(style));

        // Expanded detail view
        if app.expanded == Some(display_idx) {
            let detail_lines = build_detail_lines(entry);
            for dl in detail_lines {
                items.push(dl);
            }
        }
    }

    let list = List::new(items);
    let mut list_state = ListState::default();

    // Calculate scroll offset to keep selected visible
    let visible_height = inner.height as usize;
    if app.selected >= visible_height {
        list_state.select(Some(app.selected));
    } else {
        list_state.select(Some(app.selected));
    }

    // Use offset to scroll
    let total = items_len_for_scroll(app);
    if app.tailing && total > visible_height {
        *list_state.offset_mut() = total.saturating_sub(visible_height);
    } else if app.selected > 0 {
        let current_offset = *list_state.offset_mut();
        if app.selected < current_offset {
            *list_state.offset_mut() = app.selected;
        } else if app.selected >= current_offset + visible_height {
            *list_state.offset_mut() = app.selected.saturating_sub(visible_height) + 1;
        }
    }

    f.render_stateful_widget(list, inner, &mut list_state);
}

/// Count total display rows including expanded detail lines.
fn items_len_for_scroll(app: &App) -> usize {
    let mut count = app.filtered_indices.len();
    if let Some(expanded_idx) = app.expanded {
        if let Some(&entry_idx) = app.filtered_indices.get(expanded_idx) {
            if let Some(entry) = app.entries.get(entry_idx) {
                count += detail_line_count(entry);
            }
        }
    }
    count
}

fn detail_line_count(entry: &crate::types::TraceEntry) -> usize {
    let mut count = 1; // rule/event line
    count += 1; // input line
    if entry.rule.is_some() {
        count += 1;
    }
    count += 1; // latency line
    count += 1; // blank line
    count
}

fn build_detail_lines<'a>(entry: &crate::types::TraceEntry) -> Vec<ListItem<'a>> {
    let mut lines = Vec::new();
    let prefix = Span::styled("    \u{250a} ", Style::default().fg(DIM));

    lines.push(ListItem::new(Line::from(vec![
        prefix.clone(),
        Span::styled(format!("event: {}", entry.event), Style::default().fg(DIM)),
    ])));

    lines.push(ListItem::new(Line::from(vec![
        prefix.clone(),
        Span::styled(format!("input: {}", entry.input_summary), Style::default().fg(DIM)),
    ])));

    if let Some(ref rule) = entry.rule {
        lines.push(ListItem::new(Line::from(vec![
            prefix.clone(),
            Span::styled(format!("rule: {}", rule), Style::default().fg(DIM)),
        ])));
    }

    lines.push(ListItem::new(Line::from(vec![
        prefix.clone(),
        Span::styled(
            format!("latency: {}ms", entry.duration_ms),
            Style::default().fg(DIM),
        ),
    ])));

    lines.push(ListItem::new(Line::from(vec![
        Span::raw(""),
    ])));

    lines
}

fn draw_status_bar(f: &mut Frame, app: &App, area: Rect) {
    let total = app.entries.len();
    let allowed = app.count_by_decision("allow") + app.count_by_decision("completed");
    let approved = app.count_by_decision("approve") + app.count_by_decision("warn");
    let blocked = app.count_by_decision("block");

    let threat_text = if let Some(ref state) = app.session_state {
        if state.terminated {
            let reason = state.termination_reason.as_deref().unwrap_or("unknown");
            format!("\u{2717} TERMINATED \u{2500} {}", reason)
        } else if state.is_in_heightened_state() {
            let remaining = state
                .heightened_until_call
                .map(|u| u.saturating_sub(state.tool_call_count))
                .unwrap_or(0);
            format!("\u{26a0} heightened (watching {} more calls)", remaining)
        } else if state.suspicion_level > 0 {
            format!("warned ({})", state.warning_count)
        } else {
            "normal".to_string()
        }
    } else {
        "normal".to_string()
    };

    let is_terminated = app
        .session_state
        .as_ref()
        .map_or(false, |s| s.terminated);

    let bar_style = if is_terminated {
        Style::default().bg(Color::Red).fg(WHITE).bold()
    } else {
        Style::default().fg(DIM)
    };

    let status = Line::from(vec![
        Span::styled(format!("  {} total", total), bar_style),
        Span::styled("   ", bar_style),
        Span::styled(format!("{}", allowed), Style::default().fg(GREEN).patch(bar_style)),
        Span::styled(" \u{2713} allowed", bar_style),
        Span::styled("   ", bar_style),
        Span::styled(format!("{}", approved), Style::default().fg(YELLOW).patch(bar_style)),
        Span::styled(" \u{25cf} approved", bar_style),
        Span::styled("   ", bar_style),
        Span::styled(format!("{}", blocked), Style::default().fg(RED).patch(bar_style)),
        Span::styled(" \u{2717} blocked", bar_style),
        Span::styled(
            format!("   threat: {}", threat_text),
            if is_terminated {
                Style::default().bg(Color::Red).fg(WHITE).bold()
            } else {
                Style::default().fg(DIM)
            },
        ),
    ]);

    f.render_widget(Paragraph::new(status), area);
}

fn draw_help_overlay(f: &mut Frame, area: Rect) {
    let help_width = 40u16;
    let help_height = 14u16;
    let x = area.width.saturating_sub(help_width) / 2;
    let y = area.height.saturating_sub(help_height) / 2;
    let help_area = Rect::new(x, y, help_width.min(area.width), help_height.min(area.height));

    f.render_widget(Clear, help_area);

    let help_text = vec![
        Line::from(""),
        Line::from(vec![
            Span::styled("  j/\u{2191}  ", Style::default().fg(CYAN)),
            Span::raw("scroll up"),
        ]),
        Line::from(vec![
            Span::styled("  k/\u{2193}  ", Style::default().fg(CYAN)),
            Span::raw("scroll down"),
        ]),
        Line::from(vec![
            Span::styled("  G     ", Style::default().fg(CYAN)),
            Span::raw("jump to latest (tail)"),
        ]),
        Line::from(vec![
            Span::styled("  g     ", Style::default().fg(CYAN)),
            Span::raw("jump to start"),
        ]),
        Line::from(vec![
            Span::styled("  Enter ", Style::default().fg(CYAN)),
            Span::raw("expand/collapse detail"),
        ]),
        Line::from(vec![
            Span::styled("  f     ", Style::default().fg(CYAN)),
            Span::raw("cycle filter"),
        ]),
        Line::from(vec![
            Span::styled("  Esc   ", Style::default().fg(CYAN)),
            Span::raw("clear filter/collapse"),
        ]),
        Line::from(vec![
            Span::styled("  ?     ", Style::default().fg(CYAN)),
            Span::raw("toggle this help"),
        ]),
        Line::from(vec![
            Span::styled("  q     ", Style::default().fg(CYAN)),
            Span::raw("quit"),
        ]),
        Line::from(""),
    ];

    let help = Paragraph::new(help_text)
        .block(
            Block::default()
                .title(" keybindings ")
                .title_style(Style::default().fg(WHITE).bold())
                .borders(Borders::ALL)
                .border_type(BorderType::Rounded)
                .border_style(Style::default().fg(DIM))
                .padding(Padding::horizontal(1)),
        )
        .wrap(Wrap { trim: false });

    f.render_widget(help, help_area);
}

fn extract_time(timestamp: &str) -> String {
    // Try to extract HH:MM:SS from RFC3339 timestamp
    if let Some(t_pos) = timestamp.find('T') {
        let after_t = &timestamp[t_pos + 1..];
        if after_t.len() >= 8 {
            return after_t[..8].to_string();
        }
    }
    // Fallback: last 8 chars or full string
    if timestamp.len() >= 8 {
        timestamp[timestamp.len() - 8..].to_string()
    } else {
        timestamp.to_string()
    }
}
