//! Bottom-pane slash-command palette. Takes the editor's slot when
//! `state.bottom_pane = Some(BottomPane::Palette(...))`. Pure rendering;
//! key handling is in `update.rs::handle_palette_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::slash::PaletteState;
use crate::theme;

/// Height the palette wants: borders(2) + filter row(1) + one row per
/// filtered command (capped at 9) + hint(1).
pub fn desired_height(state: &PaletteState, extension_commands: &[String]) -> u16 {
    let rows = (state.filtered(extension_commands).len() as u16).clamp(1, 9);
    2 + 1 + rows + 1
}

pub fn render(state: &PaletteState, extension_commands: &[String], frame: &mut Frame, area: Rect) {
    let block = Block::default()
        .title(" Commands ")
        .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), // filter line
            Constraint::Min(0),    // command list
            Constraint::Length(1), // hint
        ])
        .split(inner);

    // Filter line: `/` + what the user typed.
    frame.render_widget(
        Paragraph::new(Line::from(vec![
            Span::styled("/", theme::assistant()),
            Span::raw(state.filter.clone()),
        ])),
        chunks[0],
    );

    let filtered = state.filtered(extension_commands);
    let mut body: Vec<Line> = Vec::new();
    if filtered.is_empty() {
        body.push(Line::from(Span::styled(
            "(no matching command)",
            theme::dim(),
        )));
    } else {
        for (idx, cmd) in filtered.iter().take(9).enumerate() {
            let selected = idx == state.selected;
            let marker = if selected { "› " } else { "  " };
            let name_style = if selected {
                theme::primary().add_modifier(Modifier::BOLD)
            } else if cmd.enabled {
                theme::primary()
            } else {
                theme::dim()
            };
            body.push(Line::from(vec![
                Span::styled(
                    marker,
                    if selected {
                        theme::assistant()
                    } else {
                        theme::dim()
                    },
                ),
                Span::styled(format!("/{}", cmd.name), name_style),
                Span::raw("  "),
                Span::styled(cmd.description.to_string(), theme::dim()),
            ]));
        }
    }
    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(" run  "),
            Span::styled("Esc", theme::error()),
            Span::raw(" cancel"),
        ])),
        chunks[2],
    );
}

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

    #[test]
    fn renders_full_palette() -> Result<(), Box<dyn std::error::Error>> {
        let state = PaletteState::default();
        let mut term = Terminal::new(TestBackend::new(70, 13))?;
        term.draw(|f| render(&state, &[], f, f.area()))?;
        insta::assert_snapshot!(term.backend());
        Ok(())
    }

    #[test]
    fn renders_filtered_palette() -> Result<(), Box<dyn std::error::Error>> {
        let state = PaletteState {
            filter: "co".into(),
            selected: 0,
        };
        let mut term = Terminal::new(TestBackend::new(70, 8))?;
        term.draw(|f| render(&state, &[], f, f.area()))?;
        insta::assert_snapshot!(term.backend());
        Ok(())
    }

    #[test]
    fn desired_height_scales_with_match_count() {
        let rows = (crate::slash::builtin_commands().len() as u16).clamp(1, 9);
        assert_eq!(
            desired_height(&PaletteState::default(), &[]),
            2 + 1 + rows + 1
        );
        let one = PaletteState {
            filter: "compact".into(),
            selected: 0,
        };
        assert_eq!(desired_height(&one, &[]), 2 + 1 + 1 + 1);
    }
}
