use ratatui::{
    layout::{Alignment, Constraint, Direction, Layout, Rect},
    style::{Color, Modifier, Style},
    text::{Line, Span},
    widgets::{Block, BorderType, Borders, Paragraph},
    Frame,
};
use xiaoo_api::chat::ReasoningEffort;

use crate::app::App;
use crate::app_state::RuntimeStatusLight;
use crate::status_panel::StatusPanel;

use super::utils::sanitize_terminal_text;

impl App {
    pub(crate) fn render_header(&mut self, frame: &mut Frame, area: Rect) {
        let block = Block::default()
            .borders(Borders::ALL)
            .border_type(BorderType::Rounded)
            .border_style(Style::default().fg(self.state.theme.border))
            .style(Style::default().bg(self.state.theme.background));
        let inner = block.inner(area);
        self.state.render_state.theme_toggle_area = None;
        frame.render_widget(block, area);

        if inner.width == 0 || inner.height == 0 {
            return;
        }

        let theme_button_text = format!(" {} ", self.state.theme.toggle_button_label());
        let theme_button_width = theme_button_text.chars().count() as u16;

        // Session id short form: first 8 chars of `state.session_id` (UUIDs
        // are random, so this is enough to tell apart the small handful of
        // sessions a user juggles). The full id is still shown in the
        // `/sessions` switch dialog. Read live from `state.session_id` so
        // hook-driven switches (Create/SwitchSession) and `/sessions`
        // update the header on the next redraw without any extra plumbing.
        let session_short: String = self
            .state
            .session_id
            .chars()
            .take(8)
            .collect::<String>()
            .trim()
            .to_string();
        let session_text = format!(" Sess {session_short} ");
        let session_chunk_width = session_text.chars().count() as u16;

        let inner_chunks = Layout::default()
            .direction(Direction::Horizontal)
            .constraints([
                Constraint::Length(14),
                Constraint::Length(session_chunk_width),
                Constraint::Min(1),
                Constraint::Length(theme_button_width),
                Constraint::Length(20),
            ])
            .split(inner);

        let title = Paragraph::new(Line::from(vec![Span::styled(
            " XiaoO",
            Style::default()
                .fg(self.state.theme.accent)
                .add_modifier(Modifier::BOLD),
        )]));
        frame.render_widget(title, inner_chunks[0]);

        let session_widget = Paragraph::new(Line::from(vec![
            Span::styled("Sess ", Style::default().fg(self.state.theme.muted)),
            Span::styled(
                sanitize_terminal_text(&session_short),
                Style::default()
                    .fg(self.state.theme.accent)
                    .add_modifier(Modifier::BOLD),
            ),
        ]));
        frame.render_widget(session_widget, inner_chunks[1]);

        let all_labels = self.state.agent_tab_labels();
        let active_label = self.state.active_agent_tab_label().to_string();
        let active_index = all_labels
            .iter()
            .position(|label| label == &active_label)
            .unwrap_or(0);

        let available_width = inner_chunks[2].width as usize;

        let tab_widths: Vec<usize> = all_labels
            .iter()
            .map(|label| label.chars().count() + 2)
            .collect();

        let mut visible_start = self.state.render_state.first_visible_agent_tab;

        if active_index < visible_start {
            visible_start = active_index;
        } else {
            let mut test_start = visible_start;

            while test_start <= active_index {
                let mut total_width = 0;
                let mut last_visible_index = test_start;

                for (i, width) in tab_widths[test_start..].iter().enumerate() {
                    let w = if total_width > 0 { width + 1 } else { *width };
                    if total_width + w > available_width {
                        break;
                    }
                    total_width += w;
                    last_visible_index = test_start + i;
                }

                if last_visible_index >= active_index {
                    break;
                }

                test_start += 1;
            }

            visible_start = test_start;
        }

        self.state.render_state.first_visible_agent_tab = visible_start;

        let mut tabs = Vec::new();
        let mut current_width = 0;

        for (index, label) in all_labels.iter().enumerate() {
            if index < visible_start {
                continue;
            }

            let tab_width = label.chars().count() + 2;
            let needs_space = current_width > 0;
            let additional_width = if needs_space {
                tab_width + 1
            } else {
                tab_width
            };

            if current_width + additional_width > available_width {
                break;
            }

            if needs_space {
                tabs.push(Span::raw(" "));
                current_width += 1;
            }

            tabs.push(Span::styled(
                format!(" {label} "),
                self.state.theme.tab_style(label == &active_label),
            ));
            current_width += tab_width;
        }

        if let Some(role) = self.state.active_agent_role_config() {
            if !role.description.trim().is_empty() {
                let desc = role.description.as_str();
                let desc_width = desc.chars().count() + 2;
                if current_width + desc_width <= available_width {
                    tabs.push(Span::raw("  "));
                    tabs.push(Span::styled(
                        desc,
                        Style::default().fg(self.state.theme.muted),
                    ));
                }
            }
        }
        frame.render_widget(Paragraph::new(Line::from(tabs)), inner_chunks[2]);

        let theme_button_style = Style::default()
            .fg(self.state.theme.primary)
            .add_modifier(Modifier::BOLD | Modifier::UNDERLINED);
        let theme_button = Paragraph::new(Line::from(vec![Span::styled(
            theme_button_text.clone(),
            theme_button_style,
        )]));
        frame.render_widget(theme_button, inner_chunks[3]);
        self.state.render_state.theme_toggle_area = Some(Rect {
            x: inner_chunks[3].x,
            y: inner_chunks[3].y,
            width: theme_button_width.min(inner_chunks[3].width),
            height: 1,
        });

        let now = chrono::Local::now();
        let now_text = now.format("%H:%M:%S").to_string();
        let (status_light_symbol, status_light_style, status_label, status_label_style) =
            match self.state.runtime_status_light() {
                RuntimeStatusLight::Running => {
                    let (symbol, light_style) =
                        running_status_light(now.timestamp_millis(), self.state.theme.success);
                    (
                        symbol,
                        light_style,
                        "RUN",
                        Style::default()
                            .fg(self.state.theme.success)
                            .add_modifier(Modifier::BOLD),
                    )
                }
                RuntimeStatusLight::AwaitingInteraction => (
                    "●",
                    Style::default().fg(self.state.theme.gradient_yellow),
                    "ASK",
                    Style::default()
                        .fg(self.state.theme.gradient_yellow)
                        .add_modifier(Modifier::BOLD),
                ),
                RuntimeStatusLight::Idle => (
                    "●",
                    Style::default().fg(self.state.theme.foreground),
                    "IDLE",
                    Style::default()
                        .fg(self.state.theme.foreground)
                        .add_modifier(Modifier::BOLD),
                ),
            };
        let status = Paragraph::new(Line::from(vec![
            Span::styled(
                sanitize_terminal_text(&format!("{status_light_symbol} ")),
                status_light_style,
            ),
            Span::styled(format!("{status_label} "), status_label_style),
            Span::styled(now_text, Style::default().fg(self.state.theme.muted)),
        ]))
        .alignment(Alignment::Right);
        frame.render_widget(status, inner_chunks[4]);
    }

    pub(crate) fn render_status_bar(&self, frame: &mut Frame, area: Rect) {
        let block = Block::default()
            .borders(Borders::ALL)
            .border_type(BorderType::Rounded)
            .border_style(Style::default().fg(self.state.theme.border))
            .title(Line::from(vec![Span::styled(
                " Status ",
                Style::default()
                    .fg(self.state.theme.accent)
                    .add_modifier(Modifier::BOLD),
            )]))
            .style(self.state.theme.status_bar_style());
        let inner = block.inner(area);
        frame.render_widget(block, area);

        if inner.width == 0 || inner.height == 0 {
            return;
        }

        let provider_name = if self.state.status_panel.is_connected {
            format!(
                "{}/{}",
                self.state.status_panel.provider_name, self.state.status_panel.model_name
            )
        } else {
            "Disconnected".to_string()
        };
        let workspace = if self.state.status_panel.workspace_display.is_empty() {
            sanitize_terminal_text("—")
        } else {
            self.state.status_panel.workspace_display.clone()
        };
        // Session id is shown in the header (next to the XiaoO title) so it
        // stays visible at all times and updates live when hooks or
        // `/sessions` switch the active session. The bottom status bar only
        // carries backend / provider / workspace / token telemetry.
        let summary = Line::from(vec![
            Span::styled(
                self.state.status_panel.backend_display.clone(),
                Style::default()
                    .fg(self.state.theme.primary)
                    .add_modifier(Modifier::BOLD),
            ),
            Span::styled("  ", Style::default().fg(self.state.theme.muted)),
            Span::styled(
                provider_name,
                Style::default().fg(self.state.theme.foreground),
            ),
            Span::styled("  WS ", Style::default().fg(self.state.theme.muted)),
            Span::styled(workspace, Style::default().fg(self.state.theme.foreground)),
            Span::styled("  Mem ", Style::default().fg(self.state.theme.muted)),
            Span::styled(
                self.state.status_panel.memory_status.label(),
                Style::default()
                    .fg(self.state.theme.primary)
                    .add_modifier(Modifier::BOLD),
            ),
            Span::styled("  Tok ", Style::default().fg(self.state.theme.muted)),
            Span::styled(
                StatusPanel::format_token_count(self.state.status_panel.total_tokens),
                Style::default()
                    .fg(self.state.theme.primary)
                    .add_modifier(Modifier::BOLD),
            ),
            Span::styled(" ", Style::default().fg(self.state.theme.muted)),
            Span::styled("(", Style::default().fg(self.state.theme.muted)),
            Span::styled("in ", Style::default().fg(self.state.theme.muted)),
            Span::styled(
                StatusPanel::format_token_count(self.state.status_panel.prompt_tokens),
                Style::default().fg(self.state.theme.foreground),
            ),
            Span::styled(" / ", Style::default().fg(self.state.theme.muted)),
            Span::styled("out ", Style::default().fg(self.state.theme.muted)),
            Span::styled(
                StatusPanel::format_token_count(self.state.status_panel.completion_tokens),
                Style::default().fg(self.state.theme.foreground),
            ),
            Span::styled(")  Ctx ", Style::default().fg(self.state.theme.muted)),
            Span::styled(
                StatusPanel::format_context_usage(
                    self.state.status_panel.input_context_tokens,
                    self.state.status_panel.input_context_tokens_estimated,
                ),
                Style::default()
                    .fg(self.state.theme.gradient_yellow)
                    .add_modifier(Modifier::BOLD),
            ),
            Span::styled("  Lat ", Style::default().fg(self.state.theme.muted)),
            Span::styled(
                format!("{}ms", self.state.status_panel.last_latency_ms),
                Style::default()
                    .fg(self.state.theme.accent)
                    .add_modifier(Modifier::BOLD),
            ),
            Span::styled(
                "  Think(Ctrl+T) ",
                Style::default().fg(self.state.theme.muted),
            ),
            Span::styled(
                self.state.reasoning_effort.to_string(),
                Style::default()
                    .fg(reasoning_effort_color(self.state.reasoning_effort))
                    .add_modifier(Modifier::BOLD),
            ),
        ]);
        let summary_bar = Paragraph::new(summary);
        frame.render_widget(summary_bar, inner);
    }
}

fn reasoning_effort_color(effort: ReasoningEffort) -> Color {
    match effort {
        ReasoningEffort::Off => Color::Gray,
        ReasoningEffort::High => Color::Yellow,
        ReasoningEffort::Max => Color::Red,
    }
}

fn running_status_light(now_millis: i64, color: Color) -> (&'static str, Style) {
    let bright = now_millis.rem_euclid(1_200) < 650;
    let symbol = if bright { "●" } else { "○" };
    let modifier = if bright {
        Modifier::BOLD
    } else {
        Modifier::empty()
    };
    (symbol, Style::default().fg(color).add_modifier(modifier))
}

#[cfg(test)]
mod tests {
    use ratatui::style::{Color, Modifier};

    use super::running_status_light;

    #[test]
    fn running_status_light_blinks_at_human_scale() {
        let (bright_symbol, bright_style) = running_status_light(0, Color::Green);
        let (dim_symbol, dim_style) = running_status_light(700, Color::Green);

        assert_eq!(bright_symbol, "●");
        assert!(bright_style.add_modifier.contains(Modifier::BOLD));
        assert_eq!(dim_symbol, "○");
        assert!(!dim_style.add_modifier.contains(Modifier::BOLD));
    }
}