//! Status bar rendering for the bottom two rows.
//!
//! The status bar displays simulation information and provides
//! clickable menu items for user interaction.

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

/// Data needed to render the status bar.
#[derive(Debug, Clone)]
pub struct StatusBarInfo {
    pub fps: Option<f32>,
    pub ball_count: usize,
    pub gravity_percent: i32,
    pub force_percent: i32,
    /// Each element true if that geyser (1-6) is currently active.
    pub active_geysers: [bool; 6],
    pub color_mode: bool,
}

/// Clickable button regions for mouse detection.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StatusBarButton {
    Options,
    Shapes,
    Colors,
    Clear,
    Reset,
    Save,
    Load,
    Help,
    Quit,
    Number(u8),
}

pub struct StatusBar;

impl StatusBar {
    /// Row 0: Number keys (1-6), Row 1: Status info, Row 2: Menu buttons.
    pub const HEIGHT: u16 = 3;

    pub fn render(frame: &mut Frame, area: Rect, info: &StatusBarInfo) {
        // Split into three rows
        let chunks = Layout::default()
            .direction(Direction::Vertical)
            .constraints([
                Constraint::Length(1),
                Constraint::Length(1),
                Constraint::Length(1),
            ])
            .split(area);

        // Top row: Number keys for burst effects
        let number_line = Self::build_number_line(area.width, &info.active_geysers);
        let number_widget = Paragraph::new(number_line).style(Style::default().bg(Color::DarkGray));
        frame.render_widget(number_widget, chunks[0]);

        // Middle row: Status information
        let status_line = Self::build_status_line(info);
        let status_widget = Paragraph::new(status_line).style(Style::default().bg(Color::DarkGray));
        frame.render_widget(status_widget, chunks[1]);

        // Bottom row: Menu buttons
        let menu_line = Self::build_menu_line(info.color_mode);
        let menu_widget = Paragraph::new(menu_line).style(Style::default().bg(Color::DarkGray));
        frame.render_widget(menu_widget, chunks[2]);
    }

    /// Displays digits 1-6 as full-width colored zones; active geysers show asterisks.
    fn build_number_line(width: u16, active_geysers: &[bool; 6]) -> Line<'static> {
        // Background colors for each digit: 1=Red, 2=Green, 3=Yellow, 4=Blue, 5=Magenta, 6=Cyan
        let colors = [
            Color::Red,
            Color::Green,
            Color::Yellow,
            Color::Blue,
            Color::Magenta,
            Color::Cyan,
        ];

        // Calculate how many digits fit (always 6 unless terminal is very narrow)
        let max_digits = 6.min((width / 6) as usize).max(1);

        // Calculate zone width for each digit
        let zone_width = width / max_digits as u16;
        let remainder = width % max_digits as u16;

        let mut spans = Vec::new();

        for digit in 1..=max_digits {
            let bg_color = colors[(digit - 1) % colors.len()];
            // Check if this geyser is active (digit 1-6 maps to index 0-5)
            let is_active = active_geysers[digit - 1];

            // Add extra character to some zones to fill the full width
            let extra = if digit as u16 <= remainder { 1 } else { 0 };
            let this_zone_width = zone_width + extra;

            // Build the zone content: spaces with digit centered
            // Active geysers show asterisks around the digit
            let zone_content = if is_active {
                // Active geyser: show *N* pattern with asterisks filling the space
                let digit_str = format!("*{}*", digit);
                let content_len = digit_str.len();
                let left_pad = (this_zone_width as usize).saturating_sub(content_len) / 2;
                let right_pad = (this_zone_width as usize)
                    .saturating_sub(content_len)
                    .saturating_sub(left_pad);
                format!(
                    "{}{}{}",
                    "*".repeat(left_pad),
                    digit_str,
                    "*".repeat(right_pad)
                )
            } else {
                // Normal: just the digit centered
                let digit_str = format!("{}", digit);
                let left_pad = (this_zone_width as usize).saturating_sub(1) / 2;
                let right_pad = (this_zone_width as usize)
                    .saturating_sub(1)
                    .saturating_sub(left_pad);
                format!(
                    "{}{}{}",
                    " ".repeat(left_pad),
                    digit_str,
                    " ".repeat(right_pad)
                )
            };

            // Use black foreground color for visibility
            spans.push(Span::styled(
                zone_content,
                Style::default()
                    .fg(Color::Black)
                    .bg(bg_color)
                    .add_modifier(Modifier::BOLD),
            ));
        }

        Line::from(spans)
    }

    /// Returns (max_digits, zone_width) for click position and burst width calculations.
    pub fn number_zone_info(term_width: u16) -> (u8, u16) {
        let max_digits = 6.min((term_width / 6) as u8).max(1);
        let zone_width = term_width / max_digits as u16;
        (max_digits, zone_width)
    }

    fn build_status_line(info: &StatusBarInfo) -> Line<'static> {
        let mut spans = Vec::new();

        // FPS (if enabled) - to two significant figures
        if let Some(fps) = info.fps {
            spans.push(Span::styled(
                format!(" FPS: {:.1}", fps),
                Style::default().fg(Color::Green),
            ));
            spans.push(Span::raw(" | "));
        } else {
            spans.push(Span::raw(" "));
        }

        // Ball count
        spans.push(Span::styled(
            format!("Balls: {}", info.ball_count),
            Style::default().fg(Color::Cyan),
        ));
        spans.push(Span::raw(" | "));

        // Gravity percentage
        spans.push(Span::styled(
            format!("Gravity: {}%", info.gravity_percent),
            Style::default().fg(Color::Yellow),
        ));
        spans.push(Span::raw(" | "));

        // Force percentage
        spans.push(Span::styled(
            format!("Force: {}%", info.force_percent),
            Style::default().fg(Color::Magenta),
        ));

        Line::from(spans)
    }

    fn build_menu_line(color_mode: bool) -> Line<'static> {
        let button_style = Style::default()
            .fg(Color::Black)
            .bg(Color::White)
            .add_modifier(Modifier::BOLD);

        let file_button_style = Style::default()
            .fg(Color::Black)
            .bg(Color::Cyan)
            .add_modifier(Modifier::BOLD);

        let separator = Span::raw(" ");

        // Build Colors button with each letter colored: C=red, o=green, l=yellow, o=blue, r=magenta, s=cyan
        // When Color Mode is ON, use black background; when OFF, use dark gray
        let colors_bg = if color_mode {
            Color::Black
        } else {
            Color::DarkGray
        };

        // Start with space and Colors button (first button)
        let spans = vec![
            Span::raw(" "),
            Span::styled(" ", Style::default().bg(colors_bg)),
            Span::styled(
                "C",
                Style::default()
                    .fg(Color::Red)
                    .bg(colors_bg)
                    .add_modifier(Modifier::BOLD),
            ),
            Span::styled(
                "o",
                Style::default()
                    .fg(Color::Green)
                    .bg(colors_bg)
                    .add_modifier(Modifier::BOLD),
            ),
            Span::styled(
                "l",
                Style::default()
                    .fg(Color::Yellow)
                    .bg(colors_bg)
                    .add_modifier(Modifier::BOLD),
            ),
            Span::styled(
                "o",
                Style::default()
                    .fg(Color::Blue)
                    .bg(colors_bg)
                    .add_modifier(Modifier::BOLD),
            ),
            Span::styled(
                "r",
                Style::default()
                    .fg(Color::Magenta)
                    .bg(colors_bg)
                    .add_modifier(Modifier::BOLD),
            ),
            Span::styled(
                "s",
                Style::default()
                    .fg(Color::Cyan)
                    .bg(colors_bg)
                    .add_modifier(Modifier::BOLD),
            ),
            Span::styled(" ", Style::default().bg(colors_bg)),
            separator.clone(),
            Span::styled(" [O]ptions ", button_style),
            separator.clone(),
            Span::styled(" [S]hapes ", button_style),
            separator.clone(),
            Span::styled(" Clear ", button_style),
            separator.clone(),
            Span::styled(" Save ", file_button_style),
            separator.clone(),
            Span::styled(" Load ", file_button_style),
            separator.clone(),
            Span::styled(" [R]eset ", button_style),
            separator.clone(),
            Span::styled(" [?] ", button_style),
            separator.clone(),
            Span::styled(" [Q]uit ", button_style),
            Span::raw(" "),
        ];

        Line::from(spans)
    }

    pub fn button_at(column: u16, row_in_bar: u16, term_width: u16) -> Option<StatusBarButton> {
        // Row 0: Number line (for burst effects)
        if row_in_bar == 0 {
            return Self::number_at(column, term_width);
        }

        // Row 2 (bottom): Menu buttons
        if row_in_bar != 2 {
            return None;
        }

        // Button positions (based on build_menu_line layout)
        // " Colors  [O]ptions  [S]hapes  Clear  Save  Load  [R]eset  [?]  [Q]uit "
        // Layout with single space separators:
        // - Colors: columns 1-8
        // - [O]ptions: columns 10-20
        // - [S]hapes: columns 22-31
        // - Clear: columns 33-39
        // - Save: columns 41-46
        // - Load: columns 48-53
        // - [R]eset: columns 55-63
        // - [?]: columns 65-69
        // - [Q]uit: columns 71-78

        match column {
            1..=8 => Some(StatusBarButton::Colors),
            10..=20 => Some(StatusBarButton::Options),
            22..=31 => Some(StatusBarButton::Shapes),
            33..=39 => Some(StatusBarButton::Clear),
            41..=46 => Some(StatusBarButton::Save),
            48..=53 => Some(StatusBarButton::Load),
            55..=63 => Some(StatusBarButton::Reset),
            65..=69 => Some(StatusBarButton::Help),
            71..=78 => Some(StatusBarButton::Quit),
            _ => None,
        }
    }

    fn number_at(column: u16, term_width: u16) -> Option<StatusBarButton> {
        if term_width == 0 {
            return None;
        }

        // Calculate zone layout (must match build_number_line)
        let max_digits = 6.min((term_width / 6) as u8).max(1);
        let zone_width = term_width / max_digits as u16;

        // Determine which zone the column falls into
        let zone_index = column / zone_width;

        // Clamp to valid digit range (1-6)
        let digit = (zone_index + 1).min(max_digits as u16) as u8;

        if digit >= 1 && digit <= max_digits {
            Some(StatusBarButton::Number(digit))
        } else {
            None
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_button_detection() {
        let term_width = 80;

        // Colors button (first button now)
        assert_eq!(
            StatusBar::button_at(5, 2, term_width),
            Some(StatusBarButton::Colors)
        );

        // Options button
        assert_eq!(
            StatusBar::button_at(15, 2, term_width),
            Some(StatusBarButton::Options)
        );

        // Shapes button
        assert_eq!(
            StatusBar::button_at(25, 2, term_width),
            Some(StatusBarButton::Shapes)
        );

        // Clear button
        assert_eq!(
            StatusBar::button_at(35, 2, term_width),
            Some(StatusBarButton::Clear)
        );

        // Save button
        assert_eq!(
            StatusBar::button_at(43, 2, term_width),
            Some(StatusBarButton::Save)
        );

        // Load button
        assert_eq!(
            StatusBar::button_at(50, 2, term_width),
            Some(StatusBarButton::Load)
        );

        // Reset button
        assert_eq!(
            StatusBar::button_at(58, 2, term_width),
            Some(StatusBarButton::Reset)
        );

        // Help button
        assert_eq!(
            StatusBar::button_at(67, 2, term_width),
            Some(StatusBarButton::Help)
        );

        // Quit button
        assert_eq!(
            StatusBar::button_at(74, 2, term_width),
            Some(StatusBarButton::Quit)
        );

        // No button (column out of range)
        assert_eq!(StatusBar::button_at(80, 2, term_width), None);

        // Status row (row 1) has no buttons
        assert_eq!(StatusBar::button_at(5, 1, term_width), None);
    }

    #[test]
    fn test_number_row_detection() {
        let term_width = 80;

        // Row 0 is the number row
        // With 80 width, spacing is 80 / 6 = ~13, so digit 1 is at column 6-7
        let digit_1 = StatusBar::button_at(6, 0, term_width);
        assert!(matches!(digit_1, Some(StatusBarButton::Number(1))));

        // Digit 2 should be around column 19-20
        let digit_2 = StatusBar::button_at(19, 0, term_width);
        assert!(matches!(digit_2, Some(StatusBarButton::Number(2))));
    }
}
