use crate::{
    common::Style,
    events::{TuiEvent, TuiEventHandler},
    widgets::clock::{self, ClockState, ClockWidget},
};
use crossterm::event::{Event as CrosstermEvent, KeyModifiers};
use ratatui::{
    buffer::Buffer,
    crossterm::event::KeyCode,
    layout::{Constraint, Layout, Rect},
    text::Line,
    widgets::{StatefulWidget, Widget},
};
use std::cmp::max;

pub struct TimerState {
    clock: ClockState<clock::Timer>,
    vim_motions: bool,
}

impl TimerState {
    pub fn new(clock: ClockState<clock::Timer>, vim_motions: bool) -> Self {
        Self { clock, vim_motions }
    }

    pub fn set_with_decis(&mut self, with_decis: bool) {
        self.clock.with_decis = with_decis;
    }

    pub fn get_clock(&self) -> &ClockState<clock::Timer> {
        &self.clock
    }
}

impl TuiEventHandler for TimerState {
    fn update(&mut self, event: TuiEvent) -> Option<TuiEvent> {
        let edit_mode = self.clock.is_edit_mode();
        match event {
            TuiEvent::Tick => {
                self.clock.tick();
                self.clock.update_done_count();
            }
            // EDIT mode
            TuiEvent::Crossterm(CrosstermEvent::Key(key)) if edit_mode => match key.code {
                // Skip changes
                KeyCode::Esc => {
                    // Important: set current value first
                    self.clock.set_current_value(*self.clock.get_prev_value());
                    // before toggling back to non-edit mode
                    self.clock.toggle_edit();
                }
                // Apply changes
                KeyCode::Char('s') => {
                    self.clock.toggle_edit();
                }
                // move change position to the left
                KeyCode::Left if !self.vim_motions => {
                    self.clock.edit_next();
                }
                KeyCode::Char('h') if self.vim_motions => {
                    self.clock.edit_next();
                }
                // move change position to the right
                KeyCode::Right if !self.vim_motions => {
                    self.clock.edit_prev();
                }
                KeyCode::Char('l') if self.vim_motions => {
                    self.clock.edit_prev();
                }
                KeyCode::Up if key.modifiers.contains(KeyModifiers::CONTROL) => {
                    self.clock.edit_jump_up();
                }
                KeyCode::Char('k')
                    if key.modifiers.contains(KeyModifiers::CONTROL) && self.vim_motions =>
                {
                    self.clock.edit_jump_up();
                }
                // change value up
                KeyCode::Up if !self.vim_motions => {
                    self.clock.edit_up();
                }
                KeyCode::Char('k') if self.vim_motions => {
                    self.clock.edit_up();
                }
                // change value down
                KeyCode::Down if key.modifiers.contains(KeyModifiers::CONTROL) => {
                    self.clock.edit_jump_down();
                }
                KeyCode::Char('j')
                    if key.modifiers.contains(KeyModifiers::CONTROL) && self.vim_motions =>
                {
                    self.clock.edit_jump_down();
                }
                KeyCode::Down if !self.vim_motions => {
                    self.clock.edit_down();
                }
                KeyCode::Char('j') if self.vim_motions => {
                    self.clock.edit_down();
                }
                _ => return Some(event),
            },
            // default mode
            TuiEvent::Crossterm(CrosstermEvent::Key(key)) => match key.code {
                // Toggle run/pause
                KeyCode::Char(' ') => {
                    self.clock.toggle_pause();
                }
                // reset clock
                KeyCode::Char('r') => {
                    self.clock.reset();
                }
                // enter edit mode
                KeyCode::Char('e') => {
                    self.clock.toggle_edit();
                }
                _ => return Some(event),
            },
            _ => return Some(event),
        }
        None
    }
}

pub struct Timer {
    pub style: Style,
    pub blink: bool,
}

impl StatefulWidget for Timer {
    type State = TimerState;
    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
        let clock = &mut state.clock;
        let clock_widget = ClockWidget::new(self.style, self.blink);
        let label = Line::raw((format!("Timer {}", clock.get_mode())).to_uppercase());

        let area = area.centered(
            Constraint::Length(max(
                clock_widget.get_width(clock.get_format(), clock.with_decis),
                label.width() as u16,
            )),
            Constraint::Length(clock_widget.get_height() + 1 /* height of label */),
        );
        let [v1, v2] =
            Layout::vertical(Constraint::from_lengths([clock_widget.get_height(), 1])).areas(area);

        clock_widget.render(v1, buf, clock);
        label.centered().render(v2, buf);
    }
}
