//! Saved-recording picker.

use std::io;
use std::path::PathBuf;

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 crate::config::{Config, Palette};
use crate::tui::filelist::find_recording_files;
use crate::tui::replay_screen;

pub struct PickerState {
    files: Vec<PathBuf>,
    pub selected: usize,
    pub status: Option<String>,
}

impl PickerState {
    pub fn new(files: Vec<PathBuf>) -> Self {
        PickerState {
            files,
            selected: 0,
            status: None,
        }
    }

    pub fn len(&self) -> usize {
        self.files.len()
    }

    pub fn current(&self) -> Option<&PathBuf> {
        self.files.get(self.selected)
    }

    pub fn next(&mut self) {
        if !self.files.is_empty() {
            self.selected = (self.selected + 1) % self.files.len();
        }
    }

    pub fn previous(&mut self) {
        if !self.files.is_empty() {
            self.selected = (self.selected + self.files.len() - 1) % self.files.len();
        }
    }
}

pub fn default_roots() -> Vec<PathBuf> {
    let mut roots = Vec::new();
    if let Ok(cwd) = std::env::current_dir() {
        roots.push(cwd);
    }
    let data = PathBuf::from("tests/data");
    if data.is_dir() {
        roots.push(data);
    }
    roots
}

/// Runs the picker and returns whether the whole app should quit.
pub fn run(terminal: &mut DefaultTerminal, config: &Config) -> io::Result<bool> {
    let mut state = PickerState::new(find_recording_files(&default_roots(), 3));
    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') => return Ok(true),
                KeyCode::Esc => return Ok(false),
                KeyCode::Up => {
                    state.status = None;
                    state.previous();
                }
                KeyCode::Down => {
                    state.status = None;
                    state.next();
                }
                KeyCode::Enter => {
                    if let Some(path) = state.current().cloned() {
                        match crate::replay::load_recording(&path) {
                            Ok(replay) => {
                                if crate::tui::gamemode::is_supported(&replay.gamemode) {
                                    state.status = None;
                                    if replay_screen::run(terminal, &replay, &config.palette)? {
                                        return Ok(true);
                                    }
                                } else {
                                    state.status = Some(crate::tui::gamemode::unsupported_message(
                                        &replay.gamemode,
                                    ));
                                }
                            }
                            Err(e) => {
                                state.status = Some(format!("Cannot read recording: {e}"));
                            }
                        }
                    }
                }
                _ => {}
            }
        }
    }
}

fn draw(frame: &mut Frame, state: &PickerState, palette: &Palette) {
    let mut lines: Vec<Line> = Vec::new();
    if state.len() == 0 {
        lines.push(Line::from(Span::styled(
            "No .recording files found here. Launch directly with: rsstacker replay <path>",
            Style::default().fg(palette.label),
        )));
    } else {
        for (i, path) in state.files.iter().enumerate() {
            let selected = i == state.selected;
            let marker = if selected { "> " } else { "  " };
            let style = if selected {
                Style::default()
                    .fg(palette.highlight)
                    .add_modifier(Modifier::BOLD)
            } else {
                Style::default().fg(palette.text)
            };
            lines.push(Line::from(Span::styled(
                format!("{marker}{}", path.display()),
                style,
            )));
        }
    }
    if let Some(status) = &state.status {
        lines.push(Line::from(""));
        lines.push(Line::from(Span::styled(
            status.clone(),
            Style::default().fg(palette.status),
        )));
    }
    lines.push(Line::from(""));
    lines.push(Line::from(Span::styled(
        "Up/Down move · Enter open · Esc back · q quit",
        Style::default().fg(palette.muted),
    )));
    frame.render_widget(
        Paragraph::new(Text::from(lines)).block(
            Block::default()
                .borders(Borders::ALL)
                .title(" Open recording "),
        ),
        frame.area(),
    );
}

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

    #[test]
    fn picker_navigation_wraps_and_reports_current() {
        let files = vec![PathBuf::from("a.recording"), PathBuf::from("b.recording")];
        let mut state = PickerState::new(files);
        assert_eq!(state.len(), 2);
        assert_eq!(state.current().unwrap().file_name().unwrap(), "a.recording");
        state.next();
        assert_eq!(state.current().unwrap().file_name().unwrap(), "b.recording");
        state.next();
        assert_eq!(state.current().unwrap().file_name().unwrap(), "a.recording");
        state.previous();
        assert_eq!(state.current().unwrap().file_name().unwrap(), "b.recording");
    }

    #[test]
    fn empty_picker_has_no_current() {
        let state = PickerState::new(vec![]);
        assert_eq!(state.len(), 0);
        assert!(state.current().is_none());
    }
}
