use anyhow::{anyhow, Error, Result};
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use serde::{de, Deserialize, Serialize};
use std::{collections::HashMap, fmt::Display, str::FromStr};

/// Represents a keyboard shortcut as a key code combined with modifier keys.
#[derive(Clone, PartialEq, Eq, Copy, Debug, Hash)]
pub struct KeyShortcut {
    pub key: KeyCode,
    pub modifiers: KeyModifiers,
}

impl KeyShortcut {
    /// Creates a new key shortcut from the given key code and modifiers.
    pub fn new(key: KeyCode, modifiers: KeyModifiers) -> Self {
        Self { key, modifiers }
    }
}

impl From<KeyCode> for KeyShortcut {
    fn from(value: KeyCode) -> Self {
        Self::new(value, KeyModifiers::NONE)
    }
}

impl From<&KeyEvent> for KeyShortcut {
    fn from(value: &KeyEvent) -> Self {
        Self::new(
            match value.code {
                KeyCode::Char(c) => KeyCode::Char(c.to_ascii_lowercase()),
                _ => value.code,
            },
            value.modifiers,
        )
    }
}

impl Display for KeyShortcut {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if self.modifiers != KeyModifiers::NONE {
            write!(f, "{}+", self.modifiers)?;
        }
        match self.key {
            KeyCode::F(num) => write!(f, "F{}", num),
            KeyCode::Char(c) => write!(f, "{}", c),
            _ => write!(f, "{:?}", self.key),
        }
    }
}

impl Serialize for KeyShortcut {
    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        serializer.serialize_str(&self.to_string())
    }
}

impl FromStr for KeyShortcut {
    type Err = Error;

    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
        let mut splitted = s.split('+').rev();
        let s = splitted.next().unwrap();
        let modifiers = splitted
            .map(|s| match s.to_lowercase().as_str() {
                "s" | "shift" => Ok(KeyModifiers::SHIFT),
                "c" | "ctrl" => Ok(KeyModifiers::CONTROL),
                "a" | "alt" => Ok(KeyModifiers::ALT),
                _ => Err(anyhow!("Cannot parse event entry: Unknown modifier")),
            })
            .try_fold(KeyModifiers::NONE, |acc, modifier| {
                Ok::<_, Error>(acc | modifier?)
            })?;
        Ok(match s.to_lowercase().as_str() {
            "backspace" => Self::new(KeyCode::Backspace, modifiers),
            "null" => Self::new(KeyCode::Null, modifiers),
            "esc" | "escape" => Self::new(KeyCode::Esc, modifiers),
            "capslock" => Self::new(KeyCode::CapsLock, modifiers),
            "scrolllock" => Self::new(KeyCode::ScrollLock, modifiers),
            "numlock" => Self::new(KeyCode::NumLock, modifiers),
            "printscreen" => Self::new(KeyCode::PrintScreen, modifiers),
            "pause" => Self::new(KeyCode::Pause, modifiers),
            "menu" => Self::new(KeyCode::Menu, modifiers),
            "keypadbegin" => Self::new(KeyCode::KeypadBegin, modifiers),
            "enter" => Self::new(KeyCode::Enter, modifiers),
            "left" => Self::new(KeyCode::Left, modifiers),
            "right" => Self::new(KeyCode::Right, modifiers),
            "up" => Self::new(KeyCode::Up, modifiers),
            "down" => Self::new(KeyCode::Down, modifiers),
            "home" => Self::new(KeyCode::Home, modifiers),
            "end" => Self::new(KeyCode::End, modifiers),
            "pageup" => Self::new(KeyCode::PageUp, modifiers),
            "pagedown" => Self::new(KeyCode::PageDown, modifiers),
            "tab" => Self::new(KeyCode::Tab, modifiers),
            "backtab" => Self::new(KeyCode::BackTab, modifiers),
            "delete" => Self::new(KeyCode::Delete, modifiers),
            "insert" => Self::new(KeyCode::Insert, modifiers),
            "plus" => Self::new(KeyCode::Char('+'), modifiers),
            "comma" => Self::new(KeyCode::Char(','), modifiers),
            "doubledot" => Self::new(KeyCode::Char(':'), modifiers),
            _ if s.len() == 1 => Self::new(
                KeyCode::Char(
                    (s).parse::<char>()
                        .map_err(|e| anyhow!("Cannot parse event entry: {e}"))?
                        .to_ascii_lowercase(),
                ),
                modifiers,
            ),
            _ if s.starts_with('F') && s.len() > 1 => Self::new(
                KeyCode::F(
                    (s[1..])
                        .parse()
                        .map_err(|e| anyhow!("Cannot parse event entry: {e}"))?,
                ),
                modifiers,
            ),
            _ => return Err(anyhow!("Cannot parse event entry: Unknown key")),
        })
    }
}

impl<'de> Deserialize<'de> for KeyShortcut {
    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let s: String = Deserialize::deserialize(deserializer)?;
        Self::from_str(&s).map_err(de::Error::custom)
    }
}

/// Enum representing various UI events that can be triggered.
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Copy, Debug)]
pub enum UIEvent {
    /// Exits the application and saves the UI state.
    Quit,
    /// Forces a save of the todo list to disk.
    Save,
    /// Loads the todo list from disk.
    Load,
    /// Moves focus to the left widget in the horizontal direction.
    MoveLeft,
    /// Moves focus to the right widget in the horizontal direction.
    MoveRight,
    /// Moves focus to the above widget in the vertical direction.
    MoveUp,
    /// Moves focus to the below widget in the vertical direction.
    MoveDown,
    /// Enters input mode to create a new task.
    InsertMode,
    /// Enters edit mode to modify the currently selected task.
    EditMode,
    /// Enters search mode to filter and highlight items.
    SearchMode,

    /// Clears the search query.
    CleanSearch,
    /// Moves to the next search result within the current list.
    NextSearch,
    /// Moves to the previous search result within the current list.
    PrevSearch,
    /// Moves the selection down one item in the list widget.
    ListDown,
    /// Moves the selection up one item in the list widget.
    ListUp,
    /// Moves the selection to the first item in the list widget.
    ListFirst,
    /// Moves the selection to the last item in the list widget.
    ListLast,
    /// Swaps the selected task with the one above it.
    SwapUpItem,
    /// Swaps the selected task with the one below it.
    SwapDownItem,
    /// Deletes the currently selected task.
    RemoveItem,
    /// Moves the selected task between pending and done lists.
    MoveItem,
    /// Selects the current item (toggles filter in categories, selects task in lists).
    Select,
    /// Toggles the remove filter state for the selected category.
    Remove,
    /// Opens or closes the keybindings help popup.
    ShowHelp,
    /// Represents an unmapped key with no action.
    None,
}

impl Display for UIEvent {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let s = match self {
            UIEvent::Quit => "Quit",
            UIEvent::Save => "Save",
            UIEvent::Load => "Load",
            UIEvent::MoveLeft => "Move focus left",
            UIEvent::MoveRight => "Move focus right",
            UIEvent::MoveUp => "Move focus up",
            UIEvent::MoveDown => "Move focus down",
            UIEvent::InsertMode => "Insert mode (new task)",
            UIEvent::EditMode => "Edit mode",
            UIEvent::SearchMode => "Search mode",
            UIEvent::CleanSearch => "Clear search",
            UIEvent::NextSearch => "Next search result",
            UIEvent::PrevSearch => "Previous search result",
            UIEvent::ListDown => "List down",
            UIEvent::ListUp => "List up",
            UIEvent::ListFirst => "Go to first",
            UIEvent::ListLast => "Go to last",
            UIEvent::SwapUpItem => "Swap up",
            UIEvent::SwapDownItem => "Swap down",
            UIEvent::RemoveItem => "Remove",
            UIEvent::MoveItem => "Move to done/pending",
            UIEvent::Select => "Select / toggle filter",
            UIEvent::Remove => "Remove filter",
            UIEvent::ShowHelp => "Show keybindings help",
            UIEvent::None => "No event is set",
        };
        write!(f, "{}", s)
    }
}

impl FromStr for UIEvent {
    type Err = Error;

    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
        use UIEvent::*;
        Ok(match s.to_lowercase().as_str() {
            "quit" => Quit,
            "save" => Save,
            "load" => Load,
            "moveleft" => MoveLeft,
            "moveright" => MoveRight,
            "moveup" => MoveUp,
            "movedown" => MoveDown,
            "insertmode" => InsertMode,
            "editmode" => EditMode,
            "searchmode" => SearchMode,

            "cleansearch" => CleanSearch,
            "nextsearch" => NextSearch,
            "prevsearch" => PrevSearch,
            "listdown" => ListDown,
            "listup" => ListUp,
            "listfirst" => ListFirst,
            "listlast" => ListLast,
            "swapupitem" => SwapUpItem,
            "swapdownitem" => SwapDownItem,
            "removeitem" => RemoveItem,
            "moveitem" => MoveItem,
            "select" => Select,
            "remove" => Remove,
            "showhelp" => ShowHelp,
            "none" => None,

            _ => return Err(anyhow!("Cannot parse UI event: Unknown keyword")),
        })
    }
}

/// Struct for handling UI events based on key bindings.
#[derive(Serialize, Deserialize, Default, Clone, PartialEq, Eq, Debug)]
pub struct EventHandlerUI(HashMap<KeyShortcut, UIEvent>);

impl EventHandlerUI {
    /// Get the UI event corresponding to a given key code.
    ///
    /// # Arguments
    ///
    /// * `key` - The key code to map to a UI event.
    ///
    /// # Returns
    ///
    /// The UI event corresponding to the key code.
    pub fn get_event(&self, key: &KeyEvent) -> UIEvent {
        *self.0.get(&key.into()).unwrap_or(&UIEvent::None)
    }

    /// Combines the elements of another `Vec` into the current instance,
    /// extending the current vector with the elements from the provided vector.
    pub fn combine(&mut self, other: Self) {
        self.0.extend(other.0);
    }

    /// Check if keymaps are empty.
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    /// Number of keymaps.
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Iterator of the key shortcuts.
    pub fn keys(&self) -> impl Iterator<Item = &KeyShortcut> {
        self.0.keys()
    }

    /// Returns all key-event pairs sorted by the string representation of the key.
    pub fn entries(&self) -> Vec<(&KeyShortcut, &UIEvent)> {
        let mut entries: Vec<_> = self.0.iter().collect();
        entries.sort_by_key(|(k, _)| k.to_string());
        entries
    }
}

impl FromStr for EventHandlerUI {
    type Err = Error;

    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
        let s = s.trim();

        let data = s
            .strip_prefix('[')
            .and_then(|s| s.strip_suffix(']'))
            .ok_or_else(|| anyhow!("Cannot parse UI event: Value must be in []"))?
            .trim();

        Ok(EventHandlerUI(if data.is_empty() {
            HashMap::new()
        } else {
            data.split(',')
                .map(|s| {
                    let (key, event) = s
                        .split_once(':')
                        .ok_or_else(|| anyhow!("Cannot parse event entry: Missing separator"))?;
                    Ok((KeyShortcut::from_str(key)?, UIEvent::from_str(event)?))
                })
                .collect::<Result<HashMap<_, _>>>()?
        }))
    }
}

impl Display for EventHandlerUI {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let mut maps = self
            .0
            .iter()
            .map(|(key, event)| format!("{}:{:?}", key, event))
            .collect::<Vec<_>>();
        maps.sort();
        write!(f, "[{}]", maps.join(", "))
    }
}

impl<const N: usize> From<[(KeyShortcut, UIEvent); N]> for EventHandlerUI {
    fn from(value: [(KeyShortcut, UIEvent); N]) -> Self {
        EventHandlerUI(value.into_iter().collect())
    }
}

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

    #[test]
    fn serialization() -> Result<()> {
        let event_handler = EventHandlerUI::from([
            (KeyShortcut::from(KeyCode::Char('f')), UIEvent::None),
            (KeyShortcut::from(KeyCode::CapsLock), UIEvent::MoveItem),
        ]);
        let mut events = toml::to_string(&event_handler)?
            .lines()
            .map(|l| l.to_string())
            .collect::<Vec<_>>();
        events.sort();
        assert_eq!(events, vec!["CapsLock = \"MoveItem\"", "f = \"None\""]);

        Ok(())
    }

    #[test]
    fn deserialize() -> Result<()> {
        assert_eq!(
            toml::from_str::<EventHandlerUI>("CapsLock = \"MoveItem\"\nf = \"None\"")?,
            EventHandlerUI::from([
                (KeyShortcut::from(KeyCode::Char('f')), UIEvent::None),
                (KeyShortcut::from(KeyCode::CapsLock), UIEvent::MoveItem),
            ]),
        );

        Ok(())
    }

    #[test]
    fn event_handler_ui_from_str() -> Result<()> {
        assert_eq!(
            EventHandlerUI::from_str("[]")?,
            EventHandlerUI(HashMap::new()),
        );
        assert_eq!(
            EventHandlerUI::from_str("[f:none,capslock:moveitem]")?,
            EventHandlerUI::from([
                (KeyShortcut::from(KeyCode::Char('f')), UIEvent::None),
                (KeyShortcut::from(KeyCode::CapsLock), UIEvent::MoveItem),
            ])
        );

        Ok(())
    }

    #[test]
    fn event_handler_ui_display() {
        assert_eq!(format!("{}", EventHandlerUI(HashMap::new()),), "[]");
        assert_eq!(
            format!(
                "{}",
                EventHandlerUI::from([
                    (KeyShortcut::from(KeyCode::Char('f')), UIEvent::None),
                    (KeyShortcut::from(KeyCode::CapsLock), UIEvent::MoveItem),
                ])
            ),
            "[CapsLock:MoveItem, f:None]"
        );
    }

    #[test]
    fn combine() {
        let mut base = EventHandlerUI::from([
            (KeyShortcut::from(KeyCode::Char('a')), UIEvent::ListDown),
            (KeyShortcut::from(KeyCode::Char('b')), UIEvent::ListUp),
        ]);
        let addition = EventHandlerUI::from([
            (KeyShortcut::from(KeyCode::Char('b')), UIEvent::MoveUp),
            (KeyShortcut::from(KeyCode::Char('c')), UIEvent::ListUp),
        ]);
        base.combine(addition);
        assert_eq!(
            base,
            EventHandlerUI::from([
                (KeyShortcut::from(KeyCode::Char('a')), UIEvent::ListDown),
                (KeyShortcut::from(KeyCode::Char('b')), UIEvent::MoveUp),
                (KeyShortcut::from(KeyCode::Char('c')), UIEvent::ListUp),
            ])
        );
    }

    #[test]
    fn event_entry_display() {
        assert_eq!(
            &EventHandlerUI::from([(KeyShortcut::from(KeyCode::Char('f')), UIEvent::Quit)])
                .to_string(),
            "[f:Quit]"
        );

        assert_eq!(
            &EventHandlerUI::from([(KeyShortcut::from(KeyCode::Backspace), UIEvent::Load)])
                .to_string(),
            "[Backspace:Load]"
        );

        assert_eq!(
            &EventHandlerUI::from([(KeyShortcut::from(KeyCode::F(5)), UIEvent::Save)]).to_string(),
            "[F5:Save]"
        );

        assert_eq!(
            &EventHandlerUI::from([(
                KeyShortcut::new(KeyCode::F(5), KeyModifiers::ALT),
                UIEvent::Save
            )])
            .to_string(),
            "[Alt+F5:Save]"
        );
    }

    #[test]
    fn event_entry_from_str() -> Result<()> {
        assert_eq!(
            EventHandlerUI::from([(KeyShortcut::from(KeyCode::Char('a')), UIEvent::ListUp)]),
            EventHandlerUI::from_str("[a:ListUp]")?
        );

        assert_eq!(
            EventHandlerUI::from([(KeyShortcut::from(KeyCode::Insert), UIEvent::Select)]),
            EventHandlerUI::from_str("[iNSert:select]")?
        );

        assert_eq!(
            EventHandlerUI::from([(KeyShortcut::from(KeyCode::F(6)), UIEvent::Remove)]),
            EventHandlerUI::from_str("[F6:rEmOvE]")?
        );

        assert_eq!(
            EventHandlerUI::from([(
                KeyShortcut::new(KeyCode::Char('b'), KeyModifiers::SHIFT),
                UIEvent::ListDown
            )]),
            EventHandlerUI::from_str("[S+b:ListDown]")?
        );

        assert_eq!(
            EventHandlerUI::from([(
                KeyShortcut::new(KeyCode::Char('b'), KeyModifiers::CONTROL),
                UIEvent::ListLast
            )]),
            EventHandlerUI::from_str("[Ctrl+b:ListLast]")?
        );

        assert_eq!(
            EventHandlerUI::from([(
                KeyShortcut::new(KeyCode::Char('b'), KeyModifiers::ALT),
                UIEvent::ListLast
            )]),
            EventHandlerUI::from_str("[A+B:ListLast]")?
        );

        Ok(())
    }
}
