//! Mode selection screen.

use std::io;

use ratatui::crossterm::event::{self, Event, KeyCode, KeyEventKind};
use ratatui::style::{Modifier, Style};
use ratatui::text::{Line, Span, Text};
use ratatui::widgets::{Block, Borders, Paragraph};
use ratatui::{DefaultTerminal, Frame};

use super::play_screen;
use crate::config::{Config, Palette};
use crate::modes::Mode;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum GameMode {
    Endless,
    LineRace,
    ScoreAttack,
}

impl GameMode {
    pub fn label(&self) -> &'static str {
        match self {
            GameMode::Endless => "Endless",
            GameMode::LineRace => "Line Race",
            GameMode::ScoreAttack => "Score Attack",
        }
    }

    pub fn enabled(&self) -> bool {
        true
    }

    fn to_mode(self) -> Mode {
        match self {
            GameMode::Endless => Mode::Endless,
            GameMode::LineRace => Mode::LineRace,
            GameMode::ScoreAttack => Mode::ScoreAttack,
        }
    }
}

const MODES: [GameMode; 3] = [GameMode::Endless, GameMode::LineRace, GameMode::ScoreAttack];

pub struct ModeSelectState {
    pub selected: usize,
}

impl ModeSelectState {
    pub fn items() -> &'static [GameMode] {
        &MODES
    }

    pub fn new() -> Self {
        let selected = MODES.iter().position(|m| m.enabled()).unwrap_or(0);
        ModeSelectState { selected }
    }

    pub fn current(&self) -> GameMode {
        MODES[self.selected]
    }

    pub fn next(&mut self) {
        let n = MODES.len();
        for step in 1..=n {
            let candidate = (self.selected + step) % n;
            if MODES[candidate].enabled() {
                self.selected = candidate;
                return;
            }
        }
    }

    pub fn previous(&mut self) {
        let n = MODES.len();
        for step in 1..=n {
            let candidate = (self.selected + n - step) % n;
            if MODES[candidate].enabled() {
                self.selected = candidate;
                return;
            }
        }
    }
}

impl Default for ModeSelectState {
    fn default() -> Self {
        ModeSelectState::new()
    }
}

/// Runs mode selection and returns whether the whole app should quit.
pub fn run(terminal: &mut DefaultTerminal, config: &Config) -> io::Result<bool> {
    let mut state = ModeSelectState::new();
    loop {
        terminal.draw(|frame| draw(frame, &state, &config.palette))?;
        if let Event::Key(key) = event::read()? {
            if key.kind != KeyEventKind::Press {
                continue;
            }
            match key.code {
                KeyCode::Char('q') | KeyCode::Char('Q') => return Ok(true),
                KeyCode::Esc => return Ok(false),
                KeyCode::Up => state.previous(),
                KeyCode::Down => state.next(),
                KeyCode::Enter => {
                    if play_screen::run(terminal, state.current().to_mode(), config)? {
                        return Ok(true);
                    }
                }
                _ => {}
            }
        }
    }
}

fn draw(frame: &mut Frame, state: &ModeSelectState, palette: &Palette) {
    let mut lines: Vec<Line> = vec![
        Line::from(Span::styled(
            "Select a mode",
            Style::default()
                .fg(palette.title)
                .add_modifier(Modifier::BOLD),
        )),
        Line::from(""),
    ];
    for (i, mode) in ModeSelectState::items().iter().enumerate() {
        let selected = i == state.selected;
        let marker = if selected { "> " } else { "  " };
        let mut style = if mode.enabled() {
            Style::default().fg(palette.text)
        } else {
            Style::default().fg(palette.muted)
        };
        if selected {
            style = style.add_modifier(Modifier::BOLD).fg(palette.highlight);
        }
        let suffix = if mode.enabled() {
            ""
        } else {
            "  (coming soon)"
        };
        lines.push(Line::from(Span::styled(
            format!("{marker}{}{suffix}", mode.label()),
            style,
        )));
    }
    lines.push(Line::from(""));
    lines.push(Line::from(Span::styled(
        "Up/Down move · Enter play · Esc back · q quit",
        Style::default().fg(palette.muted),
    )));
    frame.render_widget(
        Paragraph::new(Text::from(lines))
            .block(Block::default().borders(Borders::ALL).title(" Play ")),
        frame.area(),
    );
}

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

    #[test]
    fn all_modes_enabled() {
        assert!(GameMode::Endless.enabled());
        assert!(GameMode::LineRace.enabled());
        assert!(GameMode::ScoreAttack.enabled());
    }

    #[test]
    fn navigation_cycles_all_modes() {
        let mut s = ModeSelectState::new();
        assert_eq!(s.current(), GameMode::Endless);
        s.next();
        assert_eq!(s.current(), GameMode::LineRace);
        s.next();
        assert_eq!(s.current(), GameMode::ScoreAttack);
        s.next();
        assert_eq!(s.current(), GameMode::Endless);
    }
}
