//! Inline TUI editor for `~/.capo/agent/settings.toml`.
//! See spec §4.2 (state machine) and §4.1 (row inventory).

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

use crate::settings_editor::{EditCursor, FieldKind, SettingsRow, SettingsState, SettingsValue};
use crate::theme;

pub fn desired_height(state: &SettingsState) -> u16 {
    // Rows + a status/hint row at the bottom + a header row.
    let row_count = state.rows.len() as u16;
    // Cap to avoid eating the messages area on small terminals.
    (row_count + 2).clamp(10, 28)
}

pub fn render(state: &SettingsState, frame: &mut Frame, area: Rect) {
    let mut lines: Vec<Line> = Vec::new();
    lines.push(Line::from(vec![Span::styled(
        " Settings — F2 save · Esc cancel · ↑/↓ navigate · Enter edit · Space toggles bool ",
        Style::default().add_modifier(Modifier::BOLD | Modifier::REVERSED),
    )]));

    for (idx, row) in state.rows.iter().enumerate() {
        match row {
            SettingsRow::Section { label } => {
                lines.push(Line::from(vec![Span::styled(
                    format!(" [{label}]"),
                    Style::default().add_modifier(Modifier::DIM | Modifier::BOLD),
                )]));
            }
            SettingsRow::Field { key, value, .. } => {
                let is_selected = idx == state.selected;
                let in_edit = is_selected && state.edit.is_some();
                let marker = if is_selected { "▸ " } else { "  " };
                let key_span = Span::styled(
                    format!("{marker}{key}: "),
                    if is_selected {
                        Style::default()
                            .fg(Color::Cyan)
                            .add_modifier(Modifier::BOLD)
                    } else {
                        Style::default()
                    },
                );

                let value_str = if in_edit {
                    state.edit.as_ref().map_or_else(
                        || value_display(value),
                        |cursor| edit_cursor_display(cursor, &state.rows[state.selected]),
                    )
                } else {
                    value_display(value)
                };
                let value_style = if in_edit {
                    Style::default().bg(Color::DarkGray).fg(Color::White)
                } else if is_selected {
                    Style::default().add_modifier(Modifier::UNDERLINED)
                } else {
                    Style::default()
                };
                let value_span = Span::styled(value_str, value_style);

                let error_span = state
                    .errors
                    .get(&idx)
                    .map(|msg| Span::styled(format!("  ✗ {msg}"), Style::default().fg(Color::Red)));

                let mut spans = vec![key_span, value_span];
                if let Some(es) = error_span {
                    spans.push(es);
                }
                lines.push(Line::from(spans));
            }
        }
    }

    // Status / blocked-save row at the bottom.
    if let Some(reason) = &state.save_blocked_reason {
        lines.push(Line::from(vec![Span::styled(
            format!(" ! {reason}"),
            Style::default().fg(Color::Red).add_modifier(Modifier::BOLD),
        )]));
    }

    let widget = Paragraph::new(lines).style(theme::footer());
    frame.render_widget(widget, area);
}

fn value_display(value: &SettingsValue) -> String {
    match value {
        SettingsValue::String(s) => s.clone(),
        SettingsValue::U32(n) => n.to_string(),
        SettingsValue::Usize(n) => n.to_string(),
        SettingsValue::F32(f) => format!("{f:.3}"),
        SettingsValue::Bool(b) => if *b { "true" } else { "false" }.to_string(),
    }
}

fn edit_cursor_display(cursor: &EditCursor, row: &SettingsRow) -> String {
    match cursor {
        EditCursor::Text { buffer, .. } => format!("[{buffer}_]"),
        EditCursor::Toggle => "[toggle: Space]".to_string(),
        EditCursor::EnumPicker { current_idx } => {
            if let SettingsRow::Field {
                kind: FieldKind::Enum { options },
                ..
            } = row
            {
                let current = options.get(*current_idx).copied().unwrap_or("?");
                format!("[← {current} →]")
            } else {
                "[?]".to_string()
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ratatui::backend::TestBackend;
    use ratatui::Terminal;

    fn render_state(
        state: &SettingsState,
    ) -> Result<Terminal<TestBackend>, Box<dyn std::error::Error>> {
        let mut term = Terminal::new(TestBackend::new(80, 24))?;
        term.draw(|f| render(state, f, f.area()))?;
        Ok(term)
    }

    #[test]
    fn renders_clean_default_settings() -> Result<(), Box<dyn std::error::Error>> {
        let state = SettingsState::new(capo_agent::Settings::default());
        let term = render_state(&state)?;
        insta::assert_snapshot!(term.backend());
        Ok(())
    }

    #[test]
    fn renders_with_validation_error() -> Result<(), Box<dyn std::error::Error>> {
        let mut state = SettingsState::new(capo_agent::Settings::default());
        let idx = state
            .rows
            .iter()
            .position(|r| {
                matches!(
                    r,
                    SettingsRow::Field {
                        section: "model",
                        key: "name",
                        ..
                    }
                )
            })
            .ok_or("model.name row")?;
        if let SettingsRow::Field { value, .. } = &mut state.rows[idx] {
            *value = SettingsValue::String(String::new());
        }
        let _ = crate::settings_editor::try_save(&mut state);
        let term = render_state(&state)?;
        insta::assert_snapshot!(term.backend());
        Ok(())
    }
}
