use crate::{key_map::key_map::KeyMap, utils::Utils};
use crossterm::event::KeyEvent;
use std::{
    marker::PhantomData,
    time::{Duration, Instant},
};
use trie_rs::inc_search::{Answer, IncSearch, Position};

pub type KeyMapIncSearch<'a, T> = IncSearch<'a, KeyEvent, T>;

pub struct KeyMapState<T> {
    reset_time: Instant,
    reset_period: Duration,
    position: Position,
    phantom: PhantomData<T>,
}

impl<T: Clone> KeyMapState<T> {
    pub const DEFAULT_RESET_PERIOD: Duration = Duration::from_millis(250);

    #[must_use]
    pub fn new(key_map: &KeyMap<T>) -> Self {
        Self::new_with_reset_period(key_map, Self::DEFAULT_RESET_PERIOD)
    }

    #[must_use]
    pub fn new_with_reset_period(key_map: &KeyMap<T>, reset_period: Duration) -> Self {
        let reset_time = Instant::now();
        let position = Self::get_key_map_trie_root_position(key_map);
        let phantom = PhantomData;
        let mut key_map_state = Self {
            reset_time,
            reset_period,
            position,
            phantom,
        };

        key_map_state.defer_reset_time();

        key_map_state
    }

    fn defer_reset_time(&mut self) {
        self.reset_time = self.reset_period.from_now();
    }

    fn get_key_map_trie_root_position(key_map: &KeyMap<T>) -> Position {
        key_map.trie().inc_search().into()
    }

    fn resume_inc_search<'a>(&self, key_map: &'a KeyMap<T>) -> KeyMapIncSearch<'a, T> {
        IncSearch::resume(key_map.trie(), self.position)
    }

    fn set_position<P: Into<Position>>(&mut self, position: P) {
        self.position = position.into();
    }

    fn set_position_to_key_map_trie_root(&mut self, key_map: &KeyMap<T>) {
        let position = Self::get_key_map_trie_root_position(key_map);

        self.set_position(position);
    }

    fn reset(&mut self, key_map: &KeyMap<T>) {
        self.defer_reset_time();
        self.set_position_to_key_map_trie_root(key_map);
    }

    fn get_value_from_inc_search_and_reset<'a>(
        &mut self,
        inc_search: &KeyMapIncSearch<'a, T>,
        key_map: &'a KeyMap<T>,
    ) -> Option<&'a T> {
        let value_opt = inc_search.value();

        self.reset(key_map);

        value_opt
    }

    fn get_value_from_key_map_and_reset<'a>(&mut self, key_map: &'a KeyMap<T>) -> Option<&'a T> {
        let inc_search = self.resume_inc_search(key_map);

        self.get_value_from_inc_search_and_reset(&inc_search, key_map)
    }

    pub fn on_key_event<'a>(&mut self, key_map: &'a KeyMap<T>, key_event: KeyEvent) -> Option<&'a T> {
        self.defer_reset_time();

        let mut inc_search = self.resume_inc_search(key_map);

        match inc_search.query(&key_event) {
            Some(Answer::Match) => return self.get_value_from_inc_search_and_reset(&inc_search, key_map),
            Some(Answer::PrefixAndMatch | Answer::Prefix) => self.set_position(inc_search),
            None => self.set_position_to_key_map_trie_root(key_map),
        }

        None
    }

    pub fn on_tick<'a>(&mut self, key_map: &'a KeyMap<T>) -> Option<&'a T> {
        if self.reset_time.has_happened() {
            self.get_value_from_key_map_and_reset(key_map)
        } else {
            None
        }
    }

    pub fn on_key_map_update(&mut self, key_map: &KeyMap<T>) {
        self.reset(key_map);
    }
}
