#![allow(unused_imports)]
use std::collections::HashMap;
use std::fs;
use std::path::Path;

use anyhow::{Context, Result};
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct KeyCombo {
    pub code: KeyCode,
    pub modifiers: KeyModifiers,
}

impl KeyCombo {
    pub fn simple(code: KeyCode) -> Self {
        Self {
            code,
            modifiers: KeyModifiers::NONE,
        }
    }

    pub fn ctrl(code: KeyCode) -> Self {
        Self {
            code,
            modifiers: KeyModifiers::CONTROL,
        }
    }

    pub fn shift(code: KeyCode) -> Self {
        Self {
            code,
            modifiers: KeyModifiers::SHIFT,
        }
    }

    pub fn ctrl_shift(code: KeyCode) -> Self {
        Self {
            code,
            modifiers: KeyModifiers::CONTROL | KeyModifiers::SHIFT,
        }
    }

    pub fn parse(s: &str) -> Option<Self> {
        if s.is_empty() {
            return None;
        }
        let (modifiers_str, key_part) = if s == "+" {
            ("", "+")
        } else if let Some(stripped) = s.strip_suffix("++") {
            (stripped, "+")
        } else {
            match s.rfind('+') {
                Some(idx) => (&s[..idx], &s[idx + 1..]),
                None => ("", s),
            }
        };

        let mut modifiers = KeyModifiers::NONE;
        if !modifiers_str.is_empty() {
            let parts: Vec<&str> = modifiers_str.split('+').collect();
            for part in parts {
                let part_lower = part.to_lowercase();
                match part_lower.as_str() {
                    "ctrl" | "control" => modifiers |= KeyModifiers::CONTROL,
                    "shift" => modifiers |= KeyModifiers::SHIFT,
                    "alt" => modifiers |= KeyModifiers::ALT,
                    "super" | "meta" | "cmd" => modifiers |= KeyModifiers::SUPER,
                    _ => return None,
                }
            }
        }

        let code = parse_key_code(key_part)?;
        Some(Self { code, modifiers })
    }

    pub fn to_display_string(&self) -> String {
        let key = key_code_to_string(&self.code);
        let mut result = String::with_capacity(24);

        let mut need_sep = false;
        if self.modifiers.contains(KeyModifiers::CONTROL) {
            result.push_str("Ctrl");
            need_sep = true;
        }
        if self.modifiers.contains(KeyModifiers::SHIFT) {
            if need_sep {
                result.push('+');
            }
            result.push_str("Shift");
            need_sep = true;
        }
        if self.modifiers.contains(KeyModifiers::ALT) {
            if need_sep {
                result.push('+');
            }
            result.push_str("Alt");
            need_sep = true;
        }
        if self.modifiers.contains(KeyModifiers::SUPER) {
            if need_sep {
                result.push('+');
            }
            result.push_str("Super");
            need_sep = true;
        }
        if need_sep {
            result.push('+');
        }
        result.push_str(&key);

        result
    }

    pub fn matches(&self, event: &KeyEvent) -> bool {
        self.code == event.code && self.modifiers == event.modifiers
    }
}

fn parse_key_code(s: &str) -> Option<KeyCode> {
    let s_lower = s.to_lowercase();
    match s_lower.as_str() {
        "enter" | "return" => Some(KeyCode::Enter),
        "esc" | "escape" => Some(KeyCode::Esc),
        "backspace" | "bs" => Some(KeyCode::Backspace),
        "tab" => Some(KeyCode::Tab),
        "space" | " " => Some(KeyCode::Char(' ')),
        "delete" | "del" => Some(KeyCode::Delete),
        "insert" | "ins" => Some(KeyCode::Insert),
        "home" => Some(KeyCode::Home),
        "end" => Some(KeyCode::End),
        "pageup" | "pgup" => Some(KeyCode::PageUp),
        "pagedown" | "pgdn" => Some(KeyCode::PageDown),
        "up" => Some(KeyCode::Up),
        "down" => Some(KeyCode::Down),
        "left" => Some(KeyCode::Left),
        "right" => Some(KeyCode::Right),

        "f1" => Some(KeyCode::F(1)),
        "f2" => Some(KeyCode::F(2)),
        "f3" => Some(KeyCode::F(3)),
        "f4" => Some(KeyCode::F(4)),
        "f5" => Some(KeyCode::F(5)),
        "f6" => Some(KeyCode::F(6)),
        "f7" => Some(KeyCode::F(7)),
        "f8" => Some(KeyCode::F(8)),
        "f9" => Some(KeyCode::F(9)),
        "f10" => Some(KeyCode::F(10)),
        "f11" => Some(KeyCode::F(11)),
        "f12" => Some(KeyCode::F(12)),

        _ if s.len() == 1 => {
            let c = s.chars().next()?;
            Some(KeyCode::Char(c.to_ascii_lowercase()))
        }

        _ => None,
    }
}

use std::borrow::Cow;

fn key_code_to_string(code: &KeyCode) -> Cow<'static, str> {
    match code {
        KeyCode::Enter => Cow::Borrowed("Enter"),
        KeyCode::Esc => Cow::Borrowed("Esc"),
        KeyCode::Backspace => Cow::Borrowed("Backspace"),
        KeyCode::Tab => Cow::Borrowed("Tab"),
        KeyCode::Delete => Cow::Borrowed("Delete"),
        KeyCode::Insert => Cow::Borrowed("Insert"),
        KeyCode::Home => Cow::Borrowed("Home"),
        KeyCode::End => Cow::Borrowed("End"),
        KeyCode::PageUp => Cow::Borrowed("PageUp"),
        KeyCode::PageDown => Cow::Borrowed("PageDown"),
        KeyCode::Up => Cow::Borrowed("Up"),
        KeyCode::Down => Cow::Borrowed("Down"),
        KeyCode::Left => Cow::Borrowed("Left"),
        KeyCode::Right => Cow::Borrowed("Right"),
        KeyCode::F(n) => Cow::Owned(format!("F{}", n)),
        KeyCode::Char(' ') => Cow::Borrowed("Space"),
        KeyCode::Char(c) => Cow::Owned(c.to_string()),
        _ => Cow::Borrowed("?"),
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ListAction {
    MoveUp,
    MoveDown,
    MoveLeft,
    MoveRight,
    Open,
    Delete,
    Quit,
    Help,
    OpenLocation,
    CycleFocus,
    Confirm,
    Cancel,
    ToggleButton,
    NewFromTemplate,
    CreateFolder,
    CreateNote,
    RenameFolder,
    MoveNote,
    ManageTags,
    CollapseFolder,
    ExpandFolder,
    OpenCommandPalette,

    Rename,
    Duplicate,
    TogglePin,
    CycleSort,
    Search,
    JumpToTop,
    JumpToBottom,
    PageUp,
    PageDown,
    OpenTrash,
    TogglePreview,
    OpenGraph,
    OpenCanvas,
    CreatePinstar,
    ToggleSelectMode,
    ToggleSelectItem,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum EditAction {
    Quit,
    Back,
    CycleFocus,
    ToggleButton,

    SelectAll,
    Copy,
    Cut,
    Paste,
    Undo,
    Redo,
    DeleteWord,
    DeleteNextWord,
    MoveToTop,
    MoveToBottom,
    ToggleMarkdownPreview,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum HelpAction {
    Close,
    ScrollUp,
    ScrollDown,
    NextTab,
    PrevTab,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum GraphAction {
    Quit,
    PanUp,
    PanDown,
    PanLeft,
    PanRight,
    ZoomIn,
    ZoomOut,
    OpenNote,
    AutoFit,
    Help,
    ToggleSearch,
    ToggleMinimap,
    ToggleLegend,
    ToggleGrid,
    ToggleStatus,
    Refresh,
    ReloadConfig,
    TogglePreview,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum DrawAction {
    Quit,
    SelectDrawTool,
    ToggleShapeSelector,
    SelectTextTool,
    SelectEraseTool,
    ShapeSelectorUp,
    ShapeSelectorDown,
    ShapeSelectorConfirm,
    ShapeSelectorCancel,
    TextEditorConfirm,
    TextEditorCancel,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CanvasAction {
    Quit,
    Save,
    ZoomFineIn,
    ZoomFineOut,
    ZoomIn,
    ZoomOut,
    MoveLeft,
    MoveRight,
    MoveUp,
    MoveDown,
    EditOrConnect,
    OpenContextMenu,
    ToggleGrid,
    ToggleEditorPane,
    CycleFocus,
    Help,
    RenameConfirm,
    RenameCancel,
    MenuClose,
    MenuUp,
    MenuDown,
    MenuSelect,
    CloseEditor,
    CloseEditorAlt,
    ConfirmResize,
    CancelResize,
    ExtUnfocus,
    ExtToggle,
    EditorUnfocus,
    EditorFocusExt,
    EditorSyncRaw,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum BackupAction {
    Back,
    MoveDown,
    MoveUp,
    ScrollDiffDown,
    ScrollDiffUp,
    Refresh,
    EnterCommit,
    Push,
    OpenSettings,
    CycleSection,
    CancelCommit,
    ConfirmCommit,
    CloseSettings,
    NextField,
    PrevField,
    ActivateField,
    CancelEditField,
    ConfirmEditField,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ContentTreeAction {
    MoveUp,
    MoveDown,
    ToggleCollapse,
    ExpandAll,
    CollapseAll,
    Open,
    Back,
    Help,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct KeybindsToml {
    #[serde(default)]
    pub list: HashMap<ListAction, Vec<String>>,
    #[serde(default)]
    pub edit: HashMap<EditAction, Vec<String>>,
    #[serde(default)]
    pub help: HashMap<HelpAction, Vec<String>>,
    #[serde(default)]
    pub graph: HashMap<GraphAction, Vec<String>>,
    #[serde(default)]
    pub draw: HashMap<DrawAction, Vec<String>>,
    #[serde(default)]
    pub canvas: HashMap<CanvasAction, Vec<String>>,
    #[serde(default)]
    pub backup: HashMap<BackupAction, Vec<String>>,
    #[serde(default)]
    pub content_tree: HashMap<ContentTreeAction, Vec<String>>,
}

#[derive(Debug, Clone)]
pub struct Keybinds {
    pub list: HashMap<ListAction, Vec<KeyCombo>>,
    pub edit: HashMap<EditAction, Vec<KeyCombo>>,
    pub help: HashMap<HelpAction, Vec<KeyCombo>>,
    pub graph: HashMap<GraphAction, Vec<KeyCombo>>,
    pub draw: HashMap<DrawAction, Vec<KeyCombo>>,
    pub canvas: HashMap<CanvasAction, Vec<KeyCombo>>,
    pub backup: HashMap<BackupAction, Vec<KeyCombo>>,
    pub content_tree: HashMap<ContentTreeAction, Vec<KeyCombo>>,
}

impl Default for Keybinds {
    fn default() -> Self {
        let mut list = HashMap::new();
        list.insert(
            ListAction::MoveUp,
            vec![
                KeyCombo::simple(KeyCode::Up),
                KeyCombo::simple(KeyCode::Char('k')),
            ],
        );
        list.insert(
            ListAction::MoveDown,
            vec![
                KeyCombo::simple(KeyCode::Down),
                KeyCombo::simple(KeyCode::Char('j')),
            ],
        );
        list.insert(
            ListAction::MoveLeft,
            vec![
                KeyCombo::simple(KeyCode::Left),
                KeyCombo::simple(KeyCode::Char('h')),
            ],
        );
        list.insert(
            ListAction::MoveRight,
            vec![
                KeyCombo::simple(KeyCode::Right),
                KeyCombo::simple(KeyCode::Char('l')),
            ],
        );
        list.insert(ListAction::Open, vec![KeyCombo::simple(KeyCode::Enter)]);
        list.insert(
            ListAction::Delete,
            vec![
                KeyCombo::simple(KeyCode::Char('d')),
                KeyCombo::simple(KeyCode::Delete),
            ],
        );
        list.insert(ListAction::Quit, vec![KeyCombo::simple(KeyCode::Char('q'))]);
        list.insert(
            ListAction::Help,
            vec![
                KeyCombo::simple(KeyCode::Char('?')),
                KeyCombo::simple(KeyCode::F(1)),
            ],
        );
        list.insert(
            ListAction::OpenLocation,
            vec![KeyCombo::ctrl(KeyCode::Char('f'))],
        );
        list.insert(ListAction::CycleFocus, vec![KeyCombo::simple(KeyCode::Tab)]);
        list.insert(
            ListAction::Confirm,
            vec![
                KeyCombo::simple(KeyCode::Char('y')),
                KeyCombo::simple(KeyCode::Enter),
            ],
        );
        list.insert(
            ListAction::Cancel,
            vec![
                KeyCombo::simple(KeyCode::Char('n')),
                KeyCombo::simple(KeyCode::Esc),
            ],
        );
        list.insert(
            ListAction::ToggleButton,
            vec![
                KeyCombo::simple(KeyCode::Enter),
                KeyCombo::simple(KeyCode::Char(' ')),
            ],
        );
        list.insert(
            ListAction::NewFromTemplate,
            vec![KeyCombo::simple(KeyCode::Char('t'))],
        );
        list.insert(
            ListAction::CreateFolder,
            vec![KeyCombo::simple(KeyCode::Char('n'))],
        );
        list.insert(
            ListAction::CreateNote,
            vec![KeyCombo::simple(KeyCode::Char('a'))],
        );
        list.insert(
            ListAction::RenameFolder,
            vec![KeyCombo::simple(KeyCode::Char('r'))],
        );
        list.insert(
            ListAction::MoveNote,
            vec![KeyCombo::simple(KeyCode::Char('m'))],
        );
        list.insert(
            ListAction::ManageTags,
            vec![KeyCombo::simple(KeyCode::Char('.'))],
        );
        list.insert(
            ListAction::OpenCommandPalette,
            vec![
                KeyCombo::ctrl(KeyCode::Char('p')),
                KeyCombo::shift(KeyCode::Enter),
            ],
        );

        list.insert(
            ListAction::Rename,
            vec![KeyCombo::simple(KeyCode::Char('r'))],
        );
        list.insert(
            ListAction::Duplicate,
            vec![KeyCombo::simple(KeyCode::Char('y'))],
        );
        list.insert(
            ListAction::TogglePin,
            vec![KeyCombo::simple(KeyCode::Char('p'))],
        );
        list.insert(
            ListAction::CycleSort,
            vec![KeyCombo::simple(KeyCode::Char('s'))],
        );
        list.insert(
            ListAction::Search,
            vec![KeyCombo::simple(KeyCode::Char('f'))],
        );
        list.insert(
            ListAction::JumpToTop,
            vec![KeyCombo::shift(KeyCode::Char('G'))],
        );
        list.insert(ListAction::PageUp, vec![KeyCombo::ctrl(KeyCode::Char('u'))]);
        list.insert(
            ListAction::PageDown,
            vec![KeyCombo::ctrl(KeyCode::Char('d'))],
        );
        list.insert(
            ListAction::OpenTrash,
            vec![KeyCombo::shift(KeyCode::Char('T'))],
        );
        list.insert(
            ListAction::TogglePreview,
            vec![KeyCombo::shift(KeyCode::Char('P'))],
        );
        list.insert(
            ListAction::OpenGraph,
            vec![KeyCombo::ctrl(KeyCode::Char('g'))],
        );
        list.insert(
            ListAction::ToggleSelectMode,
            vec![KeyCombo::simple(KeyCode::Char('v'))],
        );
        list.insert(
            ListAction::ToggleSelectItem,
            vec![KeyCombo::simple(KeyCode::Char(' '))],
        );

        let mut edit = HashMap::new();
        edit.insert(EditAction::Back, vec![KeyCombo::simple(KeyCode::Esc)]);
        edit.insert(EditAction::CycleFocus, vec![KeyCombo::simple(KeyCode::Tab)]);
        edit.insert(
            EditAction::ToggleButton,
            vec![
                KeyCombo::simple(KeyCode::Enter),
                KeyCombo::simple(KeyCode::Char(' ')),
            ],
        );
        edit.insert(
            EditAction::SelectAll,
            vec![KeyCombo::ctrl(KeyCode::Char('a'))],
        );
        edit.insert(
            EditAction::Copy,
            vec![
                KeyCombo::ctrl(KeyCode::Char('c')),
                KeyCombo::ctrl(KeyCode::Insert),
            ],
        );
        edit.insert(
            EditAction::Cut,
            vec![
                KeyCombo::ctrl(KeyCode::Char('x')),
                KeyCombo::shift(KeyCode::Delete),
            ],
        );
        edit.insert(
            EditAction::Paste,
            vec![
                KeyCombo::ctrl(KeyCode::Char('v')),
                KeyCombo::shift(KeyCode::Insert),
            ],
        );
        edit.insert(EditAction::Undo, vec![KeyCombo::ctrl(KeyCode::Char('z'))]);
        edit.insert(
            EditAction::Redo,
            vec![
                KeyCombo::ctrl(KeyCode::Char('y')),
                KeyCombo::ctrl_shift(KeyCode::Char('z')),
            ],
        );
        edit.insert(
            EditAction::DeleteWord,
            vec![KeyCombo::ctrl(KeyCode::Backspace)],
        );
        edit.insert(
            EditAction::DeleteNextWord,
            vec![KeyCombo::ctrl(KeyCode::Delete)],
        );
        edit.insert(EditAction::MoveToTop, vec![KeyCombo::ctrl(KeyCode::Home)]);
        edit.insert(EditAction::MoveToBottom, vec![KeyCombo::ctrl(KeyCode::End)]);
        edit.insert(
            EditAction::ToggleMarkdownPreview,
            vec![KeyCombo::ctrl(KeyCode::Char('p'))],
        );

        let mut help = HashMap::new();
        help.insert(
            HelpAction::Close,
            vec![
                KeyCombo::simple(KeyCode::Esc),
                KeyCombo::simple(KeyCode::Char('q')),
                KeyCombo::simple(KeyCode::Char('?')),
                KeyCombo::simple(KeyCode::F(1)),
            ],
        );
        help.insert(
            HelpAction::NextTab,
            vec![
                KeyCombo::simple(KeyCode::Right),
                KeyCombo::simple(KeyCode::Char('l')),
                KeyCombo::simple(KeyCode::Tab),
            ],
        );
        help.insert(
            HelpAction::PrevTab,
            vec![
                KeyCombo::simple(KeyCode::Left),
                KeyCombo::simple(KeyCode::Char('h')),
                KeyCombo::simple(KeyCode::BackTab),
            ],
        );
        help.insert(
            HelpAction::ScrollUp,
            vec![
                KeyCombo::simple(KeyCode::Up),
                KeyCombo::simple(KeyCode::Char('k')),
            ],
        );
        help.insert(
            HelpAction::ScrollDown,
            vec![
                KeyCombo::simple(KeyCode::Down),
                KeyCombo::simple(KeyCode::Char('j')),
            ],
        );

        let mut graph = HashMap::new();
        graph.insert(GraphAction::Quit, vec![KeyCombo::simple(KeyCode::Esc)]);
        graph.insert(
            GraphAction::PanUp,
            vec![
                KeyCombo::simple(KeyCode::Up),
                KeyCombo::simple(KeyCode::Char('k')),
            ],
        );
        graph.insert(
            GraphAction::PanDown,
            vec![
                KeyCombo::simple(KeyCode::Down),
                KeyCombo::simple(KeyCode::Char('j')),
            ],
        );
        graph.insert(
            GraphAction::PanLeft,
            vec![
                KeyCombo::simple(KeyCode::Left),
                KeyCombo::simple(KeyCode::Char('h')),
            ],
        );
        graph.insert(
            GraphAction::PanRight,
            vec![
                KeyCombo::simple(KeyCode::Right),
                KeyCombo::simple(KeyCode::Char('l')),
            ],
        );
        graph.insert(
            GraphAction::ZoomIn,
            vec![
                KeyCombo::simple(KeyCode::Char('+')),
                KeyCombo::ctrl(KeyCode::Char('j')),
            ],
        );
        graph.insert(
            GraphAction::ZoomOut,
            vec![
                KeyCombo::simple(KeyCode::Char('-')),
                KeyCombo::ctrl(KeyCode::Char('k')),
            ],
        );
        graph.insert(
            GraphAction::OpenNote,
            vec![KeyCombo::simple(KeyCode::Enter)],
        );
        graph.insert(
            GraphAction::AutoFit,
            vec![KeyCombo::simple(KeyCode::Char('a'))],
        );
        graph.insert(
            GraphAction::Help,
            vec![
                KeyCombo::simple(KeyCode::Char('?')),
                KeyCombo::simple(KeyCode::F(1)),
            ],
        );
        graph.insert(
            GraphAction::ToggleSearch,
            vec![KeyCombo::simple(KeyCode::Char('f'))],
        );
        graph.insert(
            GraphAction::ToggleMinimap,
            vec![KeyCombo::shift(KeyCode::Char('M'))],
        );
        graph.insert(
            GraphAction::ToggleLegend,
            vec![KeyCombo::shift(KeyCode::Char('L'))],
        );
        graph.insert(
            GraphAction::ToggleGrid,
            vec![KeyCombo::shift(KeyCode::Char('G'))],
        );
        graph.insert(
            GraphAction::ToggleStatus,
            vec![KeyCombo::shift(KeyCode::Char('S'))],
        );
        graph.insert(
            GraphAction::Refresh,
            vec![KeyCombo::simple(KeyCode::Char('r'))],
        );
        graph.insert(
            GraphAction::ReloadConfig,
            vec![KeyCombo::ctrl(KeyCode::Char('r'))],
        );
        graph.insert(
            GraphAction::TogglePreview,
            vec![KeyCombo::shift(KeyCode::Char('P'))],
        );

        let mut draw = HashMap::new();
        draw.insert(DrawAction::Quit, vec![KeyCombo::simple(KeyCode::Esc)]);
        draw.insert(
            DrawAction::SelectDrawTool,
            vec![KeyCombo::simple(KeyCode::Char('d'))],
        );
        draw.insert(
            DrawAction::ToggleShapeSelector,
            vec![KeyCombo::simple(KeyCode::Char('s'))],
        );
        draw.insert(
            DrawAction::SelectTextTool,
            vec![KeyCombo::simple(KeyCode::Char('t'))],
        );
        draw.insert(
            DrawAction::SelectEraseTool,
            vec![KeyCombo::simple(KeyCode::Char('e'))],
        );
        draw.insert(
            DrawAction::ShapeSelectorUp,
            vec![KeyCombo::simple(KeyCode::Up)],
        );
        draw.insert(
            DrawAction::ShapeSelectorDown,
            vec![KeyCombo::simple(KeyCode::Down)],
        );
        draw.insert(
            DrawAction::ShapeSelectorConfirm,
            vec![KeyCombo::simple(KeyCode::Enter)],
        );
        draw.insert(
            DrawAction::ShapeSelectorCancel,
            vec![KeyCombo::simple(KeyCode::Esc)],
        );
        draw.insert(
            DrawAction::TextEditorConfirm,
            vec![KeyCombo::simple(KeyCode::Enter)],
        );
        draw.insert(
            DrawAction::TextEditorCancel,
            vec![KeyCombo::simple(KeyCode::Esc)],
        );

        let mut canvas = HashMap::new();
        canvas.insert(CanvasAction::Quit, vec![KeyCombo::simple(KeyCode::Esc)]);
        canvas.insert(CanvasAction::Save, vec![KeyCombo::ctrl(KeyCode::Char('s'))]);
        canvas.insert(
            CanvasAction::ZoomFineIn,
            vec![KeyCombo::ctrl(KeyCode::Char('j'))],
        );
        canvas.insert(
            CanvasAction::ZoomFineOut,
            vec![KeyCombo::ctrl(KeyCode::Char('k'))],
        );
        canvas.insert(
            CanvasAction::ZoomIn,
            vec![
                KeyCombo::simple(KeyCode::Char('+')),
                KeyCombo::simple(KeyCode::Char('=')),
            ],
        );
        canvas.insert(
            CanvasAction::ZoomOut,
            vec![
                KeyCombo::simple(KeyCode::Char('-')),
                KeyCombo::simple(KeyCode::Char('_')),
            ],
        );
        canvas.insert(
            CanvasAction::MoveLeft,
            vec![
                KeyCombo::simple(KeyCode::Left),
                KeyCombo::simple(KeyCode::Char('h')),
            ],
        );
        canvas.insert(
            CanvasAction::MoveRight,
            vec![
                KeyCombo::simple(KeyCode::Right),
                KeyCombo::simple(KeyCode::Char('l')),
            ],
        );
        canvas.insert(
            CanvasAction::MoveUp,
            vec![
                KeyCombo::simple(KeyCode::Up),
                KeyCombo::simple(KeyCode::Char('k')),
            ],
        );
        canvas.insert(
            CanvasAction::MoveDown,
            vec![
                KeyCombo::simple(KeyCode::Down),
                KeyCombo::simple(KeyCode::Char('j')),
            ],
        );
        canvas.insert(
            CanvasAction::EditOrConnect,
            vec![
                KeyCombo::simple(KeyCode::Char('i')),
                KeyCombo::simple(KeyCode::Enter),
            ],
        );
        canvas.insert(
            CanvasAction::OpenContextMenu,
            vec![KeyCombo::simple(KeyCode::Char('a'))],
        );
        canvas.insert(
            CanvasAction::ToggleGrid,
            vec![KeyCombo::ctrl(KeyCode::Char('g'))],
        );
        canvas.insert(
            CanvasAction::ToggleEditorPane,
            vec![KeyCombo::ctrl(KeyCode::Char('e'))],
        );
        canvas.insert(
            CanvasAction::CycleFocus,
            vec![KeyCombo::simple(KeyCode::Tab)],
        );
        canvas.insert(
            CanvasAction::Help,
            vec![KeyCombo::simple(KeyCode::Char('?'))],
        );
        canvas.insert(
            CanvasAction::RenameConfirm,
            vec![KeyCombo::simple(KeyCode::Enter)],
        );
        canvas.insert(
            CanvasAction::RenameCancel,
            vec![KeyCombo::simple(KeyCode::Esc)],
        );
        canvas.insert(
            CanvasAction::MenuClose,
            vec![KeyCombo::simple(KeyCode::Esc)],
        );
        canvas.insert(
            CanvasAction::MenuUp,
            vec![
                KeyCombo::simple(KeyCode::Up),
                KeyCombo::simple(KeyCode::Char('k')),
            ],
        );
        canvas.insert(
            CanvasAction::MenuDown,
            vec![
                KeyCombo::simple(KeyCode::Down),
                KeyCombo::simple(KeyCode::Char('j')),
            ],
        );
        canvas.insert(
            CanvasAction::MenuSelect,
            vec![KeyCombo::simple(KeyCode::Enter)],
        );
        canvas.insert(
            CanvasAction::CloseEditor,
            vec![KeyCombo::simple(KeyCode::Esc)],
        );
        canvas.insert(
            CanvasAction::CloseEditorAlt,
            vec![KeyCombo::ctrl(KeyCode::Enter)],
        );
        canvas.insert(
            CanvasAction::ConfirmResize,
            vec![KeyCombo::simple(KeyCode::Enter)],
        );
        canvas.insert(
            CanvasAction::CancelResize,
            vec![KeyCombo::simple(KeyCode::Esc)],
        );
        canvas.insert(
            CanvasAction::ExtUnfocus,
            vec![
                KeyCombo::simple(KeyCode::Esc),
                KeyCombo::simple(KeyCode::Tab),
            ],
        );
        canvas.insert(
            CanvasAction::ExtToggle,
            vec![
                KeyCombo::simple(KeyCode::Enter),
                KeyCombo::simple(KeyCode::Char(' ')),
            ],
        );
        canvas.insert(
            CanvasAction::EditorUnfocus,
            vec![KeyCombo::simple(KeyCode::Esc)],
        );
        canvas.insert(
            CanvasAction::EditorFocusExt,
            vec![KeyCombo::simple(KeyCode::Tab)],
        );
        canvas.insert(
            CanvasAction::EditorSyncRaw,
            vec![KeyCombo::ctrl(KeyCode::Char('s'))],
        );

        let mut backup = HashMap::new();
        backup.insert(BackupAction::Back, vec![KeyCombo::simple(KeyCode::Esc)]);
        backup.insert(
            BackupAction::MoveDown,
            vec![
                KeyCombo::simple(KeyCode::Char('j')),
                KeyCombo::simple(KeyCode::Down),
            ],
        );
        backup.insert(
            BackupAction::MoveUp,
            vec![
                KeyCombo::simple(KeyCode::Char('k')),
                KeyCombo::simple(KeyCode::Up),
            ],
        );
        backup.insert(
            BackupAction::ScrollDiffDown,
            vec![KeyCombo::simple(KeyCode::PageDown)],
        );
        backup.insert(
            BackupAction::ScrollDiffUp,
            vec![KeyCombo::simple(KeyCode::PageUp)],
        );
        backup.insert(
            BackupAction::Refresh,
            vec![KeyCombo::simple(KeyCode::Char('r'))],
        );
        backup.insert(
            BackupAction::EnterCommit,
            vec![KeyCombo::simple(KeyCode::Char('s'))],
        );
        backup.insert(
            BackupAction::Push,
            vec![KeyCombo::simple(KeyCode::Char('p'))],
        );
        backup.insert(
            BackupAction::OpenSettings,
            vec![KeyCombo::simple(KeyCode::Char('/'))],
        );
        backup.insert(
            BackupAction::CycleSection,
            vec![KeyCombo::simple(KeyCode::Tab)],
        );
        backup.insert(
            BackupAction::CancelCommit,
            vec![KeyCombo::simple(KeyCode::Esc)],
        );
        backup.insert(
            BackupAction::ConfirmCommit,
            vec![KeyCombo::simple(KeyCode::Enter)],
        );
        backup.insert(
            BackupAction::CloseSettings,
            vec![KeyCombo::simple(KeyCode::Esc)],
        );
        backup.insert(
            BackupAction::NextField,
            vec![
                KeyCombo::simple(KeyCode::Char('j')),
                KeyCombo::simple(KeyCode::Down),
            ],
        );
        backup.insert(
            BackupAction::PrevField,
            vec![
                KeyCombo::simple(KeyCode::Char('k')),
                KeyCombo::simple(KeyCode::Up),
            ],
        );
        backup.insert(
            BackupAction::ActivateField,
            vec![KeyCombo::simple(KeyCode::Enter)],
        );
        backup.insert(
            BackupAction::CancelEditField,
            vec![KeyCombo::simple(KeyCode::Esc)],
        );
        backup.insert(
            BackupAction::ConfirmEditField,
            vec![KeyCombo::simple(KeyCode::Enter)],
        );

        let mut content_tree = HashMap::new();
        content_tree.insert(
            ContentTreeAction::MoveUp,
            vec![
                KeyCombo::simple(KeyCode::Char('k')),
                KeyCombo::simple(KeyCode::Up),
            ],
        );
        content_tree.insert(
            ContentTreeAction::MoveDown,
            vec![
                KeyCombo::simple(KeyCode::Char('j')),
                KeyCombo::simple(KeyCode::Down),
            ],
        );
        content_tree.insert(
            ContentTreeAction::ToggleCollapse,
            vec![
                KeyCombo::simple(KeyCode::Tab),
                KeyCombo::simple(KeyCode::Left),
                KeyCombo::simple(KeyCode::Right),
            ],
        );
        content_tree.insert(
            ContentTreeAction::ExpandAll,
            vec![KeyCombo::simple(KeyCode::Char('e'))],
        );
        content_tree.insert(
            ContentTreeAction::CollapseAll,
            vec![KeyCombo::simple(KeyCode::Char('c'))],
        );
        content_tree.insert(
            ContentTreeAction::Open,
            vec![KeyCombo::simple(KeyCode::Enter)],
        );
        content_tree.insert(
            ContentTreeAction::Back,
            vec![KeyCombo::simple(KeyCode::Esc)],
        );
        content_tree.insert(
            ContentTreeAction::Help,
            vec![
                KeyCombo::simple(KeyCode::Char('?')),
                KeyCombo::simple(KeyCode::F(1)),
            ],
        );

        Self {
            list,
            edit,
            help,
            graph,
            draw,
            canvas,
            backup,
            content_tree,
        }
    }
}

impl Keybinds {
    pub fn load(path: &Path) -> Result<Self> {
        let mut keybinds = Self::default();

        if !path.exists() {
            return Ok(keybinds);
        }

        let content = fs::read_to_string(path).context("failed to read keybinds file")?;

        let toml: KeybindsToml =
            toml::from_str(&content).context("failed to parse keybinds file")?;

        for (action, combos_str) in &toml.list {
            let combos: Vec<KeyCombo> = combos_str
                .iter()
                .filter_map(|s| KeyCombo::parse(s))
                .collect();
            if !combos.is_empty() {
                keybinds.list.insert(*action, combos);
            }
        }

        for (action, combos_str) in &toml.edit {
            let combos: Vec<KeyCombo> = combos_str
                .iter()
                .filter_map(|s| KeyCombo::parse(s))
                .collect();
            if !combos.is_empty() {
                keybinds.edit.insert(*action, combos);
            }
        }

        for (action, combos_str) in &toml.help {
            let combos: Vec<KeyCombo> = combos_str
                .iter()
                .filter_map(|s| KeyCombo::parse(s))
                .collect();
            if !combos.is_empty() {
                keybinds.help.insert(*action, combos);
            }
        }

        for (action, combos_str) in &toml.graph {
            let combos: Vec<KeyCombo> = combos_str
                .iter()
                .filter_map(|s| KeyCombo::parse(s))
                .collect();
            if !combos.is_empty() {
                keybinds.graph.insert(*action, combos);
            }
        }

        for (action, combos_str) in &toml.draw {
            let combos: Vec<KeyCombo> = combos_str
                .iter()
                .filter_map(|s| KeyCombo::parse(s))
                .collect();
            if !combos.is_empty() {
                keybinds.draw.insert(*action, combos);
            }
        }

        for (action, combos_str) in &toml.canvas {
            let combos: Vec<KeyCombo> = combos_str
                .iter()
                .filter_map(|s| KeyCombo::parse(s))
                .collect();
            if !combos.is_empty() {
                keybinds.canvas.insert(*action, combos);
            }
        }

        for (action, combos_str) in &toml.backup {
            let combos: Vec<KeyCombo> = combos_str
                .iter()
                .filter_map(|s| KeyCombo::parse(s))
                .collect();
            if !combos.is_empty() {
                keybinds.backup.insert(*action, combos);
            }
        }

        for (action, combos_str) in &toml.content_tree {
            let combos: Vec<KeyCombo> = combos_str
                .iter()
                .filter_map(|s| KeyCombo::parse(s))
                .collect();
            if !combos.is_empty() {
                keybinds.content_tree.insert(*action, combos);
            }
        }
        Ok(keybinds)
    }

    pub fn save(&self, path: &Path) -> Result<()> {
        let toml = self.to_toml();
        let content = toml::to_string_pretty(&toml).context("failed to serialize keybinds")?;

        if let Some(parent) = path.parent() {
            fs::create_dir_all(parent).context("failed to create keybinds directory")?;
        }

        crate::fsutil::atomic_write(path, content.as_bytes())?;

        Ok(())
    }

    pub fn to_toml(&self) -> KeybindsToml {
        let mut toml = KeybindsToml::default();

        for (action, combos) in &self.list {
            let values: Vec<String> = combos.iter().map(KeyCombo::to_display_string).collect();
            toml.list.insert(*action, values);
        }

        for (action, combos) in &self.edit {
            let values: Vec<String> = combos.iter().map(KeyCombo::to_display_string).collect();
            toml.edit.insert(*action, values);
        }

        for (action, combos) in &self.help {
            let values: Vec<String> = combos.iter().map(KeyCombo::to_display_string).collect();
            toml.help.insert(*action, values);
        }

        for (action, combos) in &self.graph {
            let values: Vec<String> = combos.iter().map(KeyCombo::to_display_string).collect();
            toml.graph.insert(*action, values);
        }

        for (action, combos) in &self.draw {
            let values: Vec<String> = combos.iter().map(KeyCombo::to_display_string).collect();
            toml.draw.insert(*action, values);
        }

        for (action, combos) in &self.canvas {
            let values: Vec<String> = combos.iter().map(KeyCombo::to_display_string).collect();
            toml.canvas.insert(*action, values);
        }

        for (action, combos) in &self.backup {
            let values: Vec<String> = combos.iter().map(KeyCombo::to_display_string).collect();
            toml.backup.insert(*action, values);
        }

        for (action, combos) in &self.content_tree {
            let values: Vec<String> = combos.iter().map(KeyCombo::to_display_string).collect();
            toml.content_tree.insert(*action, values);
        }
        toml
    }
    pub fn matches_list(&self, action: ListAction, event: &KeyEvent) -> bool {
        self.list
            .get(&action)
            .is_some_and(|combos| combos.iter().any(|c| c.matches(event)))
    }

    pub fn matches_edit(&self, action: EditAction, event: &KeyEvent) -> bool {
        self.edit
            .get(&action)
            .is_some_and(|combos| combos.iter().any(|c| c.matches(event)))
    }

    pub fn matches_help(&self, action: HelpAction, event: &KeyEvent) -> bool {
        self.help
            .get(&action)
            .is_some_and(|combos| combos.iter().any(|c| c.matches(event)))
    }

    pub fn matches_graph(&self, action: GraphAction, event: &KeyEvent) -> bool {
        self.graph
            .get(&action)
            .is_some_and(|combos| combos.iter().any(|c| c.matches(event)))
    }

    pub fn matches_draw(&self, action: DrawAction, event: &KeyEvent) -> bool {
        self.draw
            .get(&action)
            .is_some_and(|combos| combos.iter().any(|c| c.matches(event)))
    }

    pub fn matches_canvas(&self, action: CanvasAction, event: &KeyEvent) -> bool {
        self.canvas
            .get(&action)
            .is_some_and(|combos| combos.iter().any(|c| c.matches(event)))
    }

    pub fn matches_backup(&self, action: BackupAction, event: &KeyEvent) -> bool {
        self.backup
            .get(&action)
            .is_some_and(|combos| combos.iter().any(|c| c.matches(event)))
    }

    pub fn matches_content_tree(&self, action: ContentTreeAction, event: &KeyEvent) -> bool {
        self.content_tree
            .get(&action)
            .is_some_and(|combos| combos.iter().any(|c| c.matches(event)))
    }
    pub fn list_keys_display(&self, action: ListAction) -> String {
        self.list
            .get(&action)
            .map(|combos| {
                combos
                    .iter()
                    .map(KeyCombo::to_display_string)
                    .collect::<Vec<_>>()
                    .join("/")
            })
            .unwrap_or_default()
    }

    pub fn edit_keys_display(&self, action: EditAction) -> String {
        self.edit
            .get(&action)
            .map(|combos| {
                combos
                    .iter()
                    .map(KeyCombo::to_display_string)
                    .collect::<Vec<_>>()
                    .join("/")
            })
            .unwrap_or_default()
    }

    pub fn help_keys_display(&self, action: HelpAction) -> String {
        self.help
            .get(&action)
            .map(|combos| {
                combos
                    .iter()
                    .map(KeyCombo::to_display_string)
                    .collect::<Vec<_>>()
                    .join("/")
            })
            .unwrap_or_default()
    }

    pub fn graph_keys_display(&self, action: GraphAction) -> String {
        self.graph
            .get(&action)
            .map(|combos| {
                combos
                    .iter()
                    .map(KeyCombo::to_display_string)
                    .collect::<Vec<_>>()
                    .join("/")
            })
            .unwrap_or_default()
    }

    pub fn draw_keys_display(&self, action: DrawAction) -> String {
        self.draw
            .get(&action)
            .map(|combos| {
                combos
                    .iter()
                    .map(KeyCombo::to_display_string)
                    .collect::<Vec<_>>()
                    .join("/")
            })
            .unwrap_or_default()
    }

    pub fn canvas_keys_display(&self, action: CanvasAction) -> String {
        self.canvas
            .get(&action)
            .map(|combos| {
                combos
                    .iter()
                    .map(KeyCombo::to_display_string)
                    .collect::<Vec<_>>()
                    .join("/")
            })
            .unwrap_or_default()
    }

    pub fn backup_keys_display(&self, action: BackupAction) -> String {
        self.backup
            .get(&action)
            .map(|combos| {
                combos
                    .iter()
                    .map(KeyCombo::to_display_string)
                    .collect::<Vec<_>>()
                    .join("/")
            })
            .unwrap_or_default()
    }

    pub fn content_tree_keys_display(&self, action: ContentTreeAction) -> String {
        self.content_tree
            .get(&action)
            .map(|combos| {
                combos
                    .iter()
                    .map(KeyCombo::to_display_string)
                    .collect::<Vec<_>>()
                    .join("/")
            })
            .unwrap_or_default()
    }
}

mod tests {
    use super::*;
    #[test]
    fn test_parse_key_combo_simple() {
        let combo = KeyCombo::parse("q").unwrap();
        assert_eq!(combo.code, KeyCode::Char('q'));
        assert_eq!(combo.modifiers, KeyModifiers::NONE);
    }

    #[test]
    fn test_parse_key_combo_ctrl() {
        let combo = KeyCombo::parse("Ctrl+q").unwrap();
        assert_eq!(combo.code, KeyCode::Char('q'));
        assert_eq!(combo.modifiers, KeyModifiers::CONTROL);
    }

    #[test]
    fn test_parse_key_combo_ctrl_shift() {
        let combo = KeyCombo::parse("Ctrl+Shift+z").unwrap();
        assert_eq!(combo.code, KeyCode::Char('z'));
        assert_eq!(combo.modifiers, KeyModifiers::CONTROL | KeyModifiers::SHIFT);
    }

    #[test]
    fn test_parse_special_keys() {
        assert_eq!(KeyCombo::parse("Enter").unwrap().code, KeyCode::Enter);
        assert_eq!(KeyCombo::parse("Esc").unwrap().code, KeyCode::Esc);
        assert_eq!(KeyCombo::parse("F1").unwrap().code, KeyCode::F(1));
        assert_eq!(KeyCombo::parse("Delete").unwrap().code, KeyCode::Delete);
    }

    #[test]
    fn test_key_combo_matches() {
        let combo = KeyCombo::ctrl(KeyCode::Char('q'));
        let event = KeyEvent::new(KeyCode::Char('q'), KeyModifiers::CONTROL);
        assert!(combo.matches(&event));
        let wrong_event = KeyEvent::new(KeyCode::Char('q'), KeyModifiers::NONE);
        assert!(!combo.matches(&wrong_event));
    }

    #[test]
    fn test_default_keybinds() {
        let keybinds = Keybinds::default();
        assert!(!keybinds.list.is_empty());
        assert!(!keybinds.edit.is_empty());
        assert!(!keybinds.help.is_empty());
        assert!(!keybinds.draw.is_empty());
        assert!(!keybinds.canvas.is_empty());
        assert!(!keybinds.backup.is_empty());
        assert!(!keybinds.content_tree.is_empty());

        let toml = keybinds.to_toml();
        assert!(!toml.draw.is_empty());
        assert!(!toml.canvas.is_empty());
        assert!(!toml.content_tree.is_empty());
        let temp_dir = tempfile::tempdir().unwrap();
        let path = temp_dir.path().join("keybinds.toml");
        keybinds.save(&path).unwrap();
        let loaded_keybinds = Keybinds::load(&path).unwrap();
        assert_eq!(loaded_keybinds.draw, keybinds.draw);
        assert_eq!(loaded_keybinds.canvas, keybinds.canvas);
        assert_eq!(loaded_keybinds.backup, keybinds.backup);
        assert_eq!(loaded_keybinds.content_tree, keybinds.content_tree);
    }

    #[test]
    fn test_matches_list_action() {
        let keybinds = Keybinds::default();
        let event = KeyEvent::new(KeyCode::Char('q'), KeyModifiers::NONE);
        assert!(keybinds.matches_list(ListAction::Quit, &event));
    }

    #[test]
    fn test_new_action_displays() {
        let keybinds = Keybinds::default();
        assert_eq!(keybinds.draw_keys_display(DrawAction::SelectDrawTool), "d");
        assert_eq!(keybinds.canvas_keys_display(CanvasAction::Quit), "Esc");
        assert_eq!(keybinds.canvas_keys_display(CanvasAction::Save), "Ctrl+s");
    }
}
