//! Bottom-pane `/model` picker. Pure rendering; keys in
//! `update.rs::handle_model_picker_key`.

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

use crate::state::ModelPickerState;
use crate::theme;

pub const MAX_ROWS: usize = 8;

/// The rows the picker shows for a given query: known models whose id
/// contains the query (case-insensitive), then a free-type row if the
/// query is non-empty and not already an exact known id.
pub fn rows(query: &str) -> Vec<String> {
    let q = query.to_ascii_lowercase();
    let mut out: Vec<String> = crate::slash::KNOWN_MODELS
        .iter()
        .filter(|m| q.is_empty() || m.to_ascii_lowercase().contains(&q))
        .map(|m| m.to_string())
        .collect();
    if !query.is_empty() && !crate::slash::KNOWN_MODELS.contains(&query) {
        out.push(format!("use \"{query}\""));
    }
    out
}

pub fn desired_height(state: &ModelPickerState) -> u16 {
    let n = (rows(&state.query).len().min(MAX_ROWS) as u16).clamp(1, MAX_ROWS as u16);
    2 + 1 + n + 1
}

pub fn render(state: &ModelPickerState, frame: &mut Frame, area: Rect) {
    let block = Block::default()
        .title(" Model ")
        .borders(Borders::ALL)
        .border_style(theme::modal_border());
    let inner = block.inner(area);
    frame.render_widget(block, area);

    let chunks = Layout::default()
        .direction(Direction::Vertical)
        .constraints([
            Constraint::Length(1),
            Constraint::Min(0),
            Constraint::Length(1),
        ])
        .split(inner);

    frame.render_widget(
        Paragraph::new(Line::from(vec![
            Span::styled("model: ", theme::dim()),
            Span::raw(state.query.clone()),
        ])),
        chunks[0],
    );

    let rows = rows(&state.query);
    let mut body: Vec<Line> = Vec::new();
    if rows.is_empty() {
        body.push(Line::from(Span::styled("(type a model id)", theme::dim())));
    } else {
        for (idx, m) in rows.iter().take(MAX_ROWS).enumerate() {
            let selected = idx == state.selected;
            let marker = if selected { "› " } else { "  " };
            let style = if selected {
                theme::primary().add_modifier(Modifier::BOLD)
            } else {
                theme::primary()
            };
            body.push(Line::from(vec![
                Span::styled(
                    marker,
                    if selected {
                        theme::assistant()
                    } else {
                        theme::dim()
                    },
                ),
                Span::styled(m.clone(), style),
            ]));
        }
    }
    frame.render_widget(Paragraph::new(body), chunks[1]);

    frame.render_widget(
        Paragraph::new(Line::from(vec![
            Span::styled("↑↓", theme::assistant()),
            Span::raw(" navigate  "),
            Span::styled("Enter", theme::assistant()),
            Span::raw(" switch  "),
            Span::styled("Esc", theme::error()),
            Span::raw(" cancel"),
        ])),
        chunks[2],
    );
}

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

    #[test]
    fn rows_includes_free_type_for_unknown_query() {
        let r = rows("gpt-5");
        assert!(r.iter().any(|m| m.contains("use \"gpt-5\"")));
    }

    #[test]
    fn rows_filters_known_models() {
        let r = rows("opus");
        assert!(r.iter().any(|m| m == "claude-opus-4-7"));
        assert!(!r.iter().any(|m| m == "claude-sonnet-4-6"));
    }

    #[test]
    fn renders_model_picker() -> Result<(), Box<dyn std::error::Error>> {
        let state = ModelPickerState {
            query: "claude".into(),
            selected: 0,
        };
        let mut term = Terminal::new(TestBackend::new(60, 9))?;
        term.draw(|f| render(&state, f, f.area()))?;
        insta::assert_snapshot!(term.backend());
        Ok(())
    }
}
