use ratatui::Frame;
use ratatui::layout::{Constraint, Layout, Rect};
use ratatui::style::{Modifier, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::Paragraph;

use crate::app::model::app_state::AppState;
use crate::app::model::sql_editor::modal::{HIGH_RISK_INPUT_VISIBLE_WIDTH, SqlModalStatus};
use crate::app::policy::write::sql_risk::AcknowledgeReason;
use crate::primitives::atoms::{spinner_char, text_cursor_spans};
use crate::primitives::utils::text_utils::truncate_to_width_with;
use crate::theme::ThemePalette;

pub(super) fn render_status(frame: &mut Frame, area: Rect, state: &AppState, theme: &ThemePalette) {
    if let SqlModalStatus::ConfirmingHigh {
        decision,
        input,
        target_name,
    } = state.sql_modal.status()
    {
        render_confirming_high_status(frame, area, decision, input, target_name, theme);
        return;
    }

    if let SqlModalStatus::ConfirmingRisk { reason, label } = state.sql_modal.status() {
        render_confirming_risk_status(frame, area, reason, label, theme);
        return;
    }

    let (badge_text, badge_style, status_text, status_style) = match state.sql_modal.status() {
        SqlModalStatus::Normal => {
            if let Some(ref msg) = state.messages.last_success {
                (
                    "[NORMAL]",
                    Style::default().fg(theme.semantic.text.dim),
                    format!("\u{2713} {msg}"),
                    Style::default().fg(theme.semantic.status.success),
                )
            } else {
                (
                    "[NORMAL]",
                    Style::default().fg(theme.semantic.text.dim),
                    "Ready".to_string(),
                    Style::default().fg(theme.semantic.text.dim),
                )
            }
        }
        SqlModalStatus::Editing => (
            "[INSERT]",
            Style::default()
                .fg(theme.semantic.text.accent)
                .add_modifier(Modifier::BOLD),
            "Ready".to_string(),
            Style::default().fg(theme.semantic.text.dim),
        ),
        SqlModalStatus::Running => {
            let elapsed = state
                .query
                .start_time()
                .map(|t| t.elapsed())
                .unwrap_or_default();
            let spinner = spinner_char(elapsed.as_millis());
            let elapsed_secs = elapsed.as_secs_f32();
            let status = format!("{spinner} Running {elapsed_secs:.1}s");
            (
                "[RUNNING]",
                Style::default().fg(theme.semantic.text.accent),
                status,
                Style::default().fg(theme.semantic.text.accent),
            )
        }
        SqlModalStatus::Success => {
            let msg = success_status_message(state);
            (
                "[NORMAL]",
                Style::default().fg(theme.semantic.status.success),
                msg,
                Style::default()
                    .fg(theme.semantic.status.success)
                    .add_modifier(Modifier::BOLD),
            )
        }
        SqlModalStatus::Error => {
            let msg = error_status_message(state);
            (
                "[NORMAL]",
                Style::default().fg(theme.semantic.status.error),
                msg,
                Style::default()
                    .fg(theme.semantic.status.error)
                    .add_modifier(Modifier::BOLD),
            )
        }
        SqlModalStatus::ConfirmingAnalyzeHigh { .. } => (
            "[CONFIRM]",
            Style::default()
                .fg(theme.semantic.status.error)
                .add_modifier(Modifier::BOLD),
            "Confirm ANALYZE".to_string(),
            Style::default()
                .fg(theme.semantic.status.error)
                .add_modifier(Modifier::BOLD),
        ),
        SqlModalStatus::ConfirmingAnalyzeRisk { .. } => (
            "[CONFIRM]",
            Style::default()
                .fg(theme.semantic.status.error)
                .add_modifier(Modifier::BOLD),
            "Execute ANALYZE".to_string(),
            Style::default()
                .fg(theme.semantic.status.error)
                .add_modifier(Modifier::BOLD),
        ),
        SqlModalStatus::ConfirmingHigh { .. } | SqlModalStatus::ConfirmingRisk { .. } => {
            unreachable!()
        }
    };

    let badge_display = format!(" {badge_text}");
    let badge_width = badge_display.len() as u16;
    let [badge_area, status_area] =
        Layout::horizontal([Constraint::Length(badge_width + 1), Constraint::Min(1)]).areas(area);

    let badge_line = Line::from(Span::styled(badge_display, badge_style));
    frame.render_widget(Paragraph::new(badge_line), badge_area);

    let status_display = format!("{status_text} ");
    let status_line = Line::from(vec![Span::styled(status_display, status_style)]);
    frame.render_widget(
        Paragraph::new(status_line).alignment(ratatui::layout::Alignment::Right),
        status_area,
    );
}

fn render_confirming_high_status(
    frame: &mut Frame,
    area: Rect,
    decision: &crate::app::policy::write::write_guardrails::AdhocRiskDecision,
    input: &crate::app::model::shared::text_input::TextInputState,
    name: &str,
    theme: &ThemePalette,
) {
    let error_style = Style::default().fg(theme.semantic.status.error);

    let is_match = input.content() == name;
    let warning_text = format!("\u{26a0} HIGH RISK  {}", decision.label);
    let blocked_label = "Enter blocked";
    let mut line1_spans = vec![Span::styled(warning_text.clone(), error_style)];
    if !is_match {
        let used = (warning_text.len() + blocked_label.len()) as u16;
        let padding = area.width.saturating_sub(used).max(2);
        line1_spans.push(Span::raw(" ".repeat(padding as usize)));
        line1_spans.push(Span::styled(
            blocked_label,
            Style::default().fg(theme.semantic.text.muted),
        ));
    }
    let line1 = Line::from(line1_spans);

    let prompt_fixed_len = "Confirm \"\": > ".len();
    let max_name_display =
        (area.width as usize).saturating_sub(prompt_fixed_len + HIGH_RISK_INPUT_VISIBLE_WIDTH + 2);
    let display_name = truncate_to_width_with(name, max_name_display, "\u{2026}");
    let prompt = format!("Confirm \"{display_name}\": > ");
    let visible_width = HIGH_RISK_INPUT_VISIBLE_WIDTH;
    let cursor_spans = text_cursor_spans(
        input.content(),
        input.cursor(),
        input.viewport_offset(),
        visible_width,
        theme,
    );
    let mut line2_spans = vec![Span::styled(
        prompt,
        Style::default().fg(theme.semantic.text.secondary),
    )];
    line2_spans.extend(cursor_spans);
    if is_match {
        line2_spans.push(Span::styled(
            " \u{2713}",
            Style::default().fg(theme.semantic.status.success),
        ));
    }
    let line2 = Line::from(line2_spans);

    let paragraph = Paragraph::new(vec![line1, line2]);
    frame.render_widget(paragraph, area);
}

fn render_confirming_risk_status(
    frame: &mut Frame,
    area: Rect,
    reason: &AcknowledgeReason,
    label: &str,
    theme: &ThemePalette,
) {
    let (badge_text, badge_style, explanation) = match reason {
        AcknowledgeReason::UnknownRisk => (
            format!("\u{26a0} UNKNOWN RISK  {label}"),
            Style::default().fg(theme.semantic.status.warning),
            "sabiql can't assess this statement's risk",
        ),
        AcknowledgeReason::TargetNameUnavailable => (
            format!("\u{26a0} HIGH RISK  {label}"),
            Style::default().fg(theme.semantic.status.error),
            "Can't identify target name \u{2014} review before executing",
        ),
    };

    let line1 = Line::from(Span::styled(badge_text, badge_style));
    let line2 = Line::from(Span::styled(
        explanation,
        Style::default().fg(theme.semantic.text.muted),
    ));
    frame.render_widget(Paragraph::new(vec![line1, line2]), area);
}

fn success_status_message(state: &AppState) -> String {
    let Some(snapshot) = state.sql_modal.last_adhoc_success() else {
        return "\u{2713} OK".to_string();
    };
    let time_secs = snapshot.execution_time_ms as f64 / 1000.0;

    if let Some(tag) = snapshot.command_tag.as_ref() {
        format!("\u{2713} {} ({:.2}s)", tag.display_message(), time_secs)
    } else {
        let rows_label = if snapshot.row_count == 1 {
            "row"
        } else {
            "rows"
        };
        format!(
            "\u{2713} {} {} ({:.2}s)",
            snapshot.row_count, rows_label, time_secs
        )
    }
}

fn error_status_message(state: &AppState) -> String {
    state
        .sql_modal
        .last_adhoc_error()
        .and_then(|e| e.lines().next())
        .map_or_else(
            || "\u{2717} Error".to_string(),
            |line| format!("\u{2717} {line}"),
        )
}
