//! Keybinding configuration and management.
//!
//! Provides keybinding definitions, defaults, and custom keybinding loading.
//! Supports hierarchical configuration:
//!   1. Built-in defaults (hardcoded)
//!   2. User overrides from `~/.oxi/keybindings.toml`
//!   3. Project overrides from `.oxi/keybindings.toml`
//!
//! Key notation: `"ctrl+c"`, `"alt+enter"`, `"shift+tab"`, `"f1"`, `"pageup"`, etc.

use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt;
use std::path::{Path, PathBuf};

// ---------------------------------------------------------------------------
// Key notation parsing
// ---------------------------------------------------------------------------

/// Parsed key name (modifiers handled separately).
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum KeyName {
    Char(char),
    Enter,
    Escape,
    Tab,
    Backspace,
    Delete,
    Up,
    Down,
    Left,
    Right,
    Home,
    End,
    PageUp,
    PageDown,
    Insert,
    F(u8),
}

impl fmt::Display for KeyName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            KeyName::Char(c) => write!(f, "{}", c),
            KeyName::Enter => write!(f, "enter"),
            KeyName::Escape => write!(f, "escape"),
            KeyName::Tab => write!(f, "tab"),
            KeyName::Backspace => write!(f, "backspace"),
            KeyName::Delete => write!(f, "delete"),
            KeyName::Up => write!(f, "up"),
            KeyName::Down => write!(f, "down"),
            KeyName::Left => write!(f, "left"),
            KeyName::Right => write!(f, "right"),
            KeyName::Home => write!(f, "home"),
            KeyName::End => write!(f, "end"),
            KeyName::PageUp => write!(f, "pageup"),
            KeyName::PageDown => write!(f, "pagedown"),
            KeyName::Insert => write!(f, "insert"),
            KeyName::F(n) => write!(f, "f{}", n),
        }
    }
}

/// A fully parsed key sequence with modifiers.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct KeySequence {
    pub ctrl: bool,
    pub alt: bool,
    pub shift: bool,
    pub key: KeyName,
}

impl KeySequence {
    /// Parse a key string like `"ctrl+c"`, `"alt+enter"`, `"shift+tab"`, `"ctrl+shift+f1"`.
    pub fn parse(s: &str) -> Option<Self> {
        let mut ctrl = false;
        let mut alt = false;
        let mut shift = false;
        let mut remaining = s.trim();

        // Consume modifier prefixes (order-independent for ctrl/alt/shift)
        loop {
            if let Some(rest) = remaining
                .strip_prefix("ctrl+")
                .or_else(|| remaining.strip_prefix("control+"))
            {
                ctrl = true;
                remaining = rest;
                continue;
            }
            if let Some(rest) = remaining
                .strip_prefix("alt+")
                .or_else(|| remaining.strip_prefix("meta+"))
            {
                alt = true;
                remaining = rest;
                continue;
            }
            if let Some(rest) = remaining.strip_prefix("shift+") {
                shift = true;
                remaining = rest;
                continue;
            }
            break;
        }

        let lower = remaining.to_lowercase();
        let key = match lower.as_str() {
            "enter" | "return" => KeyName::Enter,
            "escape" | "esc" => KeyName::Escape,
            "tab" => KeyName::Tab,
            "backspace" | "bs" => KeyName::Backspace,
            "delete" | "del" => KeyName::Delete,
            "insert" | "ins" => KeyName::Insert,
            "up" => KeyName::Up,
            "down" => KeyName::Down,
            "left" => KeyName::Left,
            "right" => KeyName::Right,
            "home" => KeyName::Home,
            "end" => KeyName::End,
            "pageup" | "page_up" => KeyName::PageUp,
            "pagedown" | "page_down" => KeyName::PageDown,
            // F-keys: f1..f12
            s if s.starts_with('f') && s.len() <= 3 => {
                let num: u8 = s[1..].parse().ok()?;
                if num >= 1 && num <= 12 {
                    KeyName::F(num)
                } else {
                    return None;
                }
            }
            // Single character key
            _ if remaining.len() == 1 => {
                KeyName::Char(remaining.chars().next()?)
            }
            _ => return None,
        };

        Some(Self {
            ctrl,
            alt,
            shift,
            key,
        })
    }

    /// Format the key sequence back to string notation.
    pub fn to_notation(&self) -> String {
        let mut parts = Vec::new();
        if self.ctrl {
            parts.push("ctrl");
        }
        if self.alt {
            parts.push("alt");
        }
        if self.shift {
            parts.push("shift");
        }
        let key_str = self.key.to_string();
        parts.push(&key_str);
        parts.join("+")
    }
}

impl fmt::Display for KeySequence {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.to_notation())
    }
}

// ---------------------------------------------------------------------------
// Keybinding action identifiers
// ---------------------------------------------------------------------------

/// Keybinding action identifiers.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum KeyAction {
    // Editor actions
    CursorUp,
    CursorDown,
    CursorLeft,
    CursorRight,
    CursorWordLeft,
    CursorWordRight,
    CursorLineStart,
    CursorLineEnd,
    JumpForward,
    JumpBackward,
    PageUp,
    PageDown,
    DeleteCharBackward,
    DeleteCharForward,
    DeleteWordBackward,
    DeleteWordForward,
    DeleteToLineStart,
    DeleteToLineEnd,
    Yank,
    YankPop,
    Undo,
    NewLine,

    // Input actions
    Submit,
    Tab,
    Copy,
    SelectUp,
    SelectDown,
    SelectPageUp,
    SelectPageDown,
    SelectConfirm,
    SelectCancel,
    Interrupt,

    // App actions
    Clear,
    Exit,
    Suspend,
    CycleThinkingLevel,
    CycleModelForward,
    CycleModelBackward,
    SelectModel,
    ExpandTools,
    ToggleThinking,
    ToggleSessionNamedFilter,
    ExternalEditor,
    FollowUp,
    Dequeue,
    PasteImage,
    NewSession,
    Tree,
    Fork,
    Resume,
    TreeFoldOrUp,
    TreeUnfoldOrDown,
    TreeEditLabel,
    TreeToggleLabelTimestamp,
    ToggleSessionPath,
    ToggleSessionSort,
    RenameSession,
    DeleteSession,
    DeleteSessionNoninvasive,
    SaveModelSelection,
    EnableAllModels,
    ClearAllModels,
    ToggleProvider,
    ReorderUp,
    ReorderDown,
    TreeFilterDefault,
    TreeFilterNoTools,
    TreeFilterUserOnly,
    TreeFilterLabeledOnly,
    TreeFilterAll,
    TreeFilterCycleForward,
    TreeFilterCycleBackward,

    // Custom action (for extensions)
    Custom(String),
}

impl fmt::Display for KeyAction {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            KeyAction::Custom(name) => write!(f, "{}", name),
            _ => write!(f, "{:?}", self),
        }
    }
}

impl From<&str> for KeyAction {
    fn from(s: &str) -> Self {
        match s {
            // Editor actions
            "tui.editor.cursorUp" => KeyAction::CursorUp,
            "tui.editor.cursorDown" => KeyAction::CursorDown,
            "tui.editor.cursorLeft" => KeyAction::CursorLeft,
            "tui.editor.cursorRight" => KeyAction::CursorRight,
            "tui.editor.cursorWordLeft" => KeyAction::CursorWordLeft,
            "tui.editor.cursorWordRight" => KeyAction::CursorWordRight,
            "tui.editor.cursorLineStart" => KeyAction::CursorLineStart,
            "tui.editor.cursorLineEnd" => KeyAction::CursorLineEnd,
            "tui.editor.jumpForward" => KeyAction::JumpForward,
            "tui.editor.jumpBackward" => KeyAction::JumpBackward,
            "tui.editor.pageUp" => KeyAction::PageUp,
            "tui.editor.pageDown" => KeyAction::PageDown,
            "tui.editor.deleteCharBackward" => KeyAction::DeleteCharBackward,
            "tui.editor.deleteCharForward" => KeyAction::DeleteCharForward,
            "tui.editor.deleteWordBackward" => KeyAction::DeleteWordBackward,
            "tui.editor.deleteWordForward" => KeyAction::DeleteWordForward,
            "tui.editor.deleteToLineStart" => KeyAction::DeleteToLineStart,
            "tui.editor.deleteToLineEnd" => KeyAction::DeleteToLineEnd,
            "tui.editor.yank" => KeyAction::Yank,
            "tui.editor.yankPop" => KeyAction::YankPop,
            "tui.editor.undo" => KeyAction::Undo,
            "tui.input.newLine" => KeyAction::NewLine,
            "tui.input.submit" => KeyAction::Submit,
            "tui.input.tab" => KeyAction::Tab,
            "tui.input.copy" => KeyAction::Copy,
            "tui.select.up" => KeyAction::SelectUp,
            "tui.select.down" => KeyAction::SelectDown,
            "tui.select.pageUp" => KeyAction::SelectPageUp,
            "tui.select.pageDown" => KeyAction::SelectPageDown,
            "tui.select.confirm" => KeyAction::SelectConfirm,
            "tui.select.cancel" => KeyAction::SelectCancel,

            // App actions
            "app.interrupt" => KeyAction::Interrupt,
            "app.clear" => KeyAction::Clear,
            "app.exit" => KeyAction::Exit,
            "app.suspend" => KeyAction::Suspend,
            "app.thinking.cycle" => KeyAction::CycleThinkingLevel,
            "app.model.cycleForward" => KeyAction::CycleModelForward,
            "app.model.cycleBackward" => KeyAction::CycleModelBackward,
            "app.model.select" => KeyAction::SelectModel,
            "app.tools.expand" => KeyAction::ExpandTools,
            "app.thinking.toggle" => KeyAction::ToggleThinking,
            "app.session.toggleNamedFilter" => KeyAction::ToggleSessionNamedFilter,
            "app.editor.external" => KeyAction::ExternalEditor,
            "app.message.followUp" => KeyAction::FollowUp,
            "app.message.dequeue" => KeyAction::Dequeue,
            "app.clipboard.pasteImage" => KeyAction::PasteImage,
            "app.session.new" => KeyAction::NewSession,
            "app.session.tree" => KeyAction::Tree,
            "app.session.fork" => KeyAction::Fork,
            "app.session.resume" => KeyAction::Resume,
            "app.tree.foldOrUp" => KeyAction::TreeFoldOrUp,
            "app.tree.unfoldOrDown" => KeyAction::TreeUnfoldOrDown,
            "app.tree.editLabel" => KeyAction::TreeEditLabel,
            "app.tree.toggleLabelTimestamp" => KeyAction::TreeToggleLabelTimestamp,
            "app.session.togglePath" => KeyAction::ToggleSessionPath,
            "app.session.toggleSort" => KeyAction::ToggleSessionSort,
            "app.session.rename" => KeyAction::RenameSession,
            "app.session.delete" => KeyAction::DeleteSession,
            "app.session.deleteNoninvasive" => KeyAction::DeleteSessionNoninvasive,
            "app.models.save" => KeyAction::SaveModelSelection,
            "app.models.enableAll" => KeyAction::EnableAllModels,
            "app.models.clearAll" => KeyAction::ClearAllModels,
            "app.models.toggleProvider" => KeyAction::ToggleProvider,
            "app.models.reorderUp" => KeyAction::ReorderUp,
            "app.models.reorderDown" => KeyAction::ReorderDown,
            "app.tree.filter.default" => KeyAction::TreeFilterDefault,
            "app.tree.filter.noTools" => KeyAction::TreeFilterNoTools,
            "app.tree.filter.userOnly" => KeyAction::TreeFilterUserOnly,
            "app.tree.filter.labeledOnly" => KeyAction::TreeFilterLabeledOnly,
            "app.tree.filter.all" => KeyAction::TreeFilterAll,
            "app.tree.filter.cycleForward" => KeyAction::TreeFilterCycleForward,
            "app.tree.filter.cycleBackward" => KeyAction::TreeFilterCycleBackward,

            // Short aliases
            "submit" => KeyAction::Submit,
            "cancel" => KeyAction::SelectCancel,
            "historyUp" | "history_up" => KeyAction::SelectUp,
            "historyDown" | "history_down" => KeyAction::SelectDown,

            // Fallback to custom
            _ => KeyAction::Custom(s.to_string()),
        }
    }
}

// ---------------------------------------------------------------------------
// Keybinding definition and defaults
// ---------------------------------------------------------------------------

/// A keybinding definition with default keys and description.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct KeyBinding {
    /// The action identifier (e.g. "app.interrupt")
    pub action: String,
    /// Default key sequences
    pub default_keys: Vec<String>,
    /// Human-readable description
    pub description: String,
}

impl KeyBinding {
    pub fn new(action: &str, default_keys: Vec<&str>, description: &str) -> Self {
        Self {
            action: action.to_string(),
            default_keys: default_keys.into_iter().map(String::from).collect(),
            description: description.to_string(),
        }
    }
}

/// Complete set of default keybindings matching pi-mono's KEYBINDINGS.
pub fn default_keybindings() -> HashMap<String, KeyBinding> {
    let mut m = HashMap::new();

    // --- TUI Editor ---
    m.insert("tui.editor.cursorUp".into(), KeyBinding::new("tui.editor.cursorUp", vec!["up"], "Move cursor up"));
    m.insert("tui.editor.cursorDown".into(), KeyBinding::new("tui.editor.cursorDown", vec!["down"], "Move cursor down"));
    m.insert("tui.editor.cursorLeft".into(), KeyBinding::new("tui.editor.cursorLeft", vec!["left", "ctrl+b"], "Move cursor left"));
    m.insert("tui.editor.cursorRight".into(), KeyBinding::new("tui.editor.cursorRight", vec!["right", "ctrl+f"], "Move cursor right"));
    m.insert("tui.editor.cursorWordLeft".into(), KeyBinding::new("tui.editor.cursorWordLeft", vec!["alt+left", "ctrl+left", "alt+b"], "Move cursor word left"));
    m.insert("tui.editor.cursorWordRight".into(), KeyBinding::new("tui.editor.cursorWordRight", vec!["alt+right", "ctrl+right", "alt+f"], "Move cursor word right"));
    m.insert("tui.editor.cursorLineStart".into(), KeyBinding::new("tui.editor.cursorLineStart", vec!["home", "ctrl+a"], "Move to line start"));
    m.insert("tui.editor.cursorLineEnd".into(), KeyBinding::new("tui.editor.cursorLineEnd", vec!["end", "ctrl+e"], "Move to line end"));
    m.insert("tui.editor.jumpForward".into(), KeyBinding::new("tui.editor.jumpForward", vec!["ctrl+]"], "Jump forward to character"));
    m.insert("tui.editor.jumpBackward".into(), KeyBinding::new("tui.editor.jumpBackward", vec!["ctrl+alt+]"], "Jump backward to character"));
    m.insert("tui.editor.pageUp".into(), KeyBinding::new("tui.editor.pageUp", vec!["pageup"], "Page up"));
    m.insert("tui.editor.pageDown".into(), KeyBinding::new("tui.editor.pageDown", vec!["pagedown"], "Page down"));
    m.insert("tui.editor.deleteCharBackward".into(), KeyBinding::new("tui.editor.deleteCharBackward", vec!["backspace"], "Delete character backward"));
    m.insert("tui.editor.deleteCharForward".into(), KeyBinding::new("tui.editor.deleteCharForward", vec!["delete", "ctrl+d"], "Delete character forward"));
    m.insert("tui.editor.deleteWordBackward".into(), KeyBinding::new("tui.editor.deleteWordBackward", vec!["ctrl+w", "alt+backspace"], "Delete word backward"));
    m.insert("tui.editor.deleteWordForward".into(), KeyBinding::new("tui.editor.deleteWordForward", vec!["alt+d", "alt+delete"], "Delete word forward"));
    m.insert("tui.editor.deleteToLineStart".into(), KeyBinding::new("tui.editor.deleteToLineStart", vec!["ctrl+u"], "Delete to line start"));
    m.insert("tui.editor.deleteToLineEnd".into(), KeyBinding::new("tui.editor.deleteToLineEnd", vec!["ctrl+k"], "Delete to line end"));
    m.insert("tui.editor.yank".into(), KeyBinding::new("tui.editor.yank", vec!["ctrl+y"], "Yank"));
    m.insert("tui.editor.yankPop".into(), KeyBinding::new("tui.editor.yankPop", vec!["alt+y"], "Yank pop"));
    m.insert("tui.editor.undo".into(), KeyBinding::new("tui.editor.undo", vec!["ctrl+-"], "Undo"));

    // --- TUI Input ---
    m.insert("tui.input.newLine".into(), KeyBinding::new("tui.input.newLine", vec!["shift+enter"], "Insert newline"));
    m.insert("tui.input.submit".into(), KeyBinding::new("tui.input.submit", vec!["enter"], "Submit input"));
    m.insert("tui.input.tab".into(), KeyBinding::new("tui.input.tab", vec!["tab"], "Tab / autocomplete"));
    m.insert("tui.input.copy".into(), KeyBinding::new("tui.input.copy", vec!["ctrl+c"], "Copy selection"));

    // --- TUI Select ---
    m.insert("tui.select.up".into(), KeyBinding::new("tui.select.up", vec!["up"], "Move selection up"));
    m.insert("tui.select.down".into(), KeyBinding::new("tui.select.down", vec!["down"], "Move selection down"));
    m.insert("tui.select.pageUp".into(), KeyBinding::new("tui.select.pageUp", vec!["pageup"], "Selection page up"));
    m.insert("tui.select.pageDown".into(), KeyBinding::new("tui.select.pageDown", vec!["pagedown"], "Selection page down"));
    m.insert("tui.select.confirm".into(), KeyBinding::new("tui.select.confirm", vec!["enter"], "Confirm selection"));
    m.insert("tui.select.cancel".into(), KeyBinding::new("tui.select.cancel", vec!["escape", "ctrl+c"], "Cancel selection"));

    // --- App actions ---
    m.insert("app.interrupt".into(), KeyBinding::new("app.interrupt", vec!["escape"], "Cancel or abort"));
    m.insert("app.clear".into(), KeyBinding::new("app.clear", vec!["ctrl+c"], "Clear editor"));
    m.insert("app.exit".into(), KeyBinding::new("app.exit", vec!["ctrl+d"], "Exit when editor is empty"));
    m.insert("app.suspend".into(), KeyBinding::new("app.suspend", vec!["ctrl+z"], "Suspend to background"));
    m.insert("app.thinking.cycle".into(), KeyBinding::new("app.thinking.cycle", vec!["shift+tab"], "Cycle thinking level"));
    m.insert("app.model.cycleForward".into(), KeyBinding::new("app.model.cycleForward", vec!["ctrl+p"], "Cycle to next model"));
    m.insert("app.model.cycleBackward".into(), KeyBinding::new("app.model.cycleBackward", vec!["shift+ctrl+p"], "Cycle to previous model"));
    m.insert("app.model.select".into(), KeyBinding::new("app.model.select", vec!["ctrl+l"], "Open model selector"));
    m.insert("app.tools.expand".into(), KeyBinding::new("app.tools.expand", vec!["ctrl+o"], "Toggle tool output"));
    m.insert("app.thinking.toggle".into(), KeyBinding::new("app.thinking.toggle", vec!["ctrl+t"], "Toggle thinking blocks"));
    m.insert("app.session.toggleNamedFilter".into(), KeyBinding::new("app.session.toggleNamedFilter", vec!["ctrl+n"], "Toggle named session filter"));
    m.insert("app.editor.external".into(), KeyBinding::new("app.editor.external", vec!["ctrl+g"], "Open external editor"));
    m.insert("app.message.followUp".into(), KeyBinding::new("app.message.followUp", vec!["alt+enter"], "Queue follow-up message"));
    m.insert("app.message.dequeue".into(), KeyBinding::new("app.message.dequeue", vec!["alt+up"], "Restore queued messages"));
    m.insert("app.clipboard.pasteImage".into(), KeyBinding::new("app.clipboard.pasteImage", vec!["ctrl+v"], "Paste image from clipboard"));
    m.insert("app.session.new".into(), KeyBinding::new("app.session.new", vec![], "Start a new session"));
    m.insert("app.session.tree".into(), KeyBinding::new("app.session.tree", vec![], "Open session tree"));
    m.insert("app.session.fork".into(), KeyBinding::new("app.session.fork", vec![], "Fork current session"));
    m.insert("app.session.resume".into(), KeyBinding::new("app.session.resume", vec![], "Resume a session"));
    m.insert("app.tree.foldOrUp".into(), KeyBinding::new("app.tree.foldOrUp", vec!["ctrl+left", "alt+left"], "Fold tree branch or move up"));
    m.insert("app.tree.unfoldOrDown".into(), KeyBinding::new("app.tree.unfoldOrDown", vec!["ctrl+right", "alt+right"], "Unfold tree branch or move down"));
    m.insert("app.tree.editLabel".into(), KeyBinding::new("app.tree.editLabel", vec!["shift+l"], "Edit tree label"));
    m.insert("app.tree.toggleLabelTimestamp".into(), KeyBinding::new("app.tree.toggleLabelTimestamp", vec!["shift+t"], "Toggle tree label timestamps"));
    m.insert("app.session.togglePath".into(), KeyBinding::new("app.session.togglePath", vec!["ctrl+p"], "Toggle session path display"));
    m.insert("app.session.toggleSort".into(), KeyBinding::new("app.session.toggleSort", vec!["ctrl+s"], "Toggle session sort mode"));
    m.insert("app.session.rename".into(), KeyBinding::new("app.session.rename", vec!["ctrl+r"], "Rename session"));
    m.insert("app.session.delete".into(), KeyBinding::new("app.session.delete", vec!["ctrl+d"], "Delete session"));
    m.insert("app.session.deleteNoninvasive".into(), KeyBinding::new("app.session.deleteNoninvasive", vec!["ctrl+backspace"], "Delete session when query is empty"));
    m.insert("app.models.save".into(), KeyBinding::new("app.models.save", vec!["ctrl+s"], "Save model selection"));
    m.insert("app.models.enableAll".into(), KeyBinding::new("app.models.enableAll", vec!["ctrl+a"], "Enable all models"));
    m.insert("app.models.clearAll".into(), KeyBinding::new("app.models.clearAll", vec!["ctrl+x"], "Clear all models"));
    m.insert("app.models.toggleProvider".into(), KeyBinding::new("app.models.toggleProvider", vec!["ctrl+p"], "Toggle all models for provider"));
    m.insert("app.models.reorderUp".into(), KeyBinding::new("app.models.reorderUp", vec!["alt+up"], "Move model up in order"));
    m.insert("app.models.reorderDown".into(), KeyBinding::new("app.models.reorderDown", vec!["alt+down"], "Move model down in order"));
    m.insert("app.tree.filter.default".into(), KeyBinding::new("app.tree.filter.default", vec!["ctrl+d"], "Tree filter: default view"));
    m.insert("app.tree.filter.noTools".into(), KeyBinding::new("app.tree.filter.noTools", vec!["ctrl+t"], "Tree filter: hide tool results"));
    m.insert("app.tree.filter.userOnly".into(), KeyBinding::new("app.tree.filter.userOnly", vec!["ctrl+u"], "Tree filter: user messages only"));
    m.insert("app.tree.filter.labeledOnly".into(), KeyBinding::new("app.tree.filter.labeledOnly", vec!["ctrl+l"], "Tree filter: labeled entries only"));
    m.insert("app.tree.filter.all".into(), KeyBinding::new("app.tree.filter.all", vec!["ctrl+a"], "Tree filter: show all entries"));
    m.insert("app.tree.filter.cycleForward".into(), KeyBinding::new("app.tree.filter.cycleForward", vec!["ctrl+o"], "Tree filter: cycle forward"));
    m.insert("app.tree.filter.cycleBackward".into(), KeyBinding::new("app.tree.filter.cycleBackward", vec!["shift+ctrl+o"], "Tree filter: cycle backward"));

    m
}

// ---------------------------------------------------------------------------
// TOML config format
// ---------------------------------------------------------------------------

/// TOML keybindings configuration file format.
///
/// Example `keybindings.toml`:
/// ```toml
/// [keybindings]
/// "app.interrupt" = "escape"
/// "app.clear" = "ctrl+c"
/// "tui.editor.cursorUp" = ["up", "k"]
/// ```
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KeybindingsFile {
    /// User overrides for keybinding actions.
    /// A string value means a single key; an array means multiple keys.
    #[serde(default)]
    pub keybindings: HashMap<String, TomlKeyBinding>,
}

/// A keybinding value in TOML: either a single key string or an array.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum TomlKeyBinding {
    Single(String),
    Multiple(Vec<String>),
}

impl TomlKeyBinding {
    /// Convert to a flat list of key strings.
    pub fn to_vec(&self) -> Vec<String> {
        match self {
            TomlKeyBinding::Single(s) => vec![s.clone()],
            TomlKeyBinding::Multiple(v) => v.clone(),
        }
    }
}

// ---------------------------------------------------------------------------
// Conflict detection
// ---------------------------------------------------------------------------

/// A detected keybinding conflict: the same key assigned to multiple actions.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct KeybindingConflict {
    /// The key sequence that conflicts.
    pub key: String,
    /// The actions that all bind to this key.
    pub actions: Vec<String>,
}

// ---------------------------------------------------------------------------
// Keybindings manager
// ---------------------------------------------------------------------------

/// Keybindings manager: loads defaults, merges user/project overrides,
/// resolves effective keys, and detects conflicts.
pub struct KeybindingsManager {
    /// All defined keybindings (defaults).
    bindings: HashMap<String, KeyBinding>,
    /// User overrides (action → keys).
    user_overrides: HashMap<String, Vec<String>>,
    /// Project overrides (action → keys).
    project_overrides: HashMap<String, Vec<String>>,
    /// Detected conflicts.
    conflicts: Vec<KeybindingConflict>,
}

impl KeybindingsManager {
    /// Create a new manager with defaults only.
    pub fn new() -> Self {
        let mut mgr = Self {
            bindings: default_keybindings(),
            user_overrides: HashMap::new(),
            project_overrides: HashMap::new(),
            conflicts: Vec::new(),
        };
        mgr.detect_conflicts();
        mgr
    }

    /// Create from hierarchical config: user + project paths.
    ///
    /// Priority: project overrides > user overrides > defaults.
    pub fn from_paths(user_path: Option<&Path>, project_path: Option<&Path>) -> Self {
        let mut mgr = Self::new();

        // Load user-level overrides (~/.oxi/keybindings.toml)
        if let Some(path) = user_path {
            if let Ok(keys) = Self::load_toml_overrides(path) {
                mgr.user_overrides = keys;
            } else if let Ok(keys) = Self::load_json_overrides(path) {
                // Fallback: legacy JSON format
                mgr.user_overrides = keys;
            }
        }

        // Load project-level overrides (.oxi/keybindings.toml)
        if let Some(path) = project_path {
            if let Ok(keys) = Self::load_toml_overrides(path) {
                mgr.project_overrides = keys;
            }
        }

        mgr.detect_conflicts();
        mgr
    }

    /// Convenience: create from the default user path.
    pub fn from_default_user_path() -> Self {
        let user_path = default_keybindings_path();
        Self::from_paths(user_path.as_deref(), None)
    }

    /// Register a new keybinding definition.
    pub fn register(&mut self, binding: KeyBinding) {
        self.bindings.insert(binding.action.clone(), binding);
        self.detect_conflicts();
    }

    /// Set user override for an action.
    pub fn set_user_override(&mut self, action: &str, keys: Vec<String>) {
        self.user_overrides.insert(action.to_string(), keys);
        self.detect_conflicts();
    }

    /// Set project override for an action.
    pub fn set_project_override(&mut self, action: &str, keys: Vec<String>) {
        self.project_overrides.insert(action.to_string(), keys);
        self.detect_conflicts();
    }

    /// Get the resolved key sequences for an action.
    /// Priority: project > user > defaults.
    pub fn get_keys(&self, action: &str) -> Vec<String> {
        if let Some(keys) = self.project_overrides.get(action) {
            return keys.clone();
        }
        if let Some(keys) = self.user_overrides.get(action) {
            return keys.clone();
        }
        if let Some(binding) = self.bindings.get(action) {
            return binding.default_keys.clone();
        }
        Vec::new()
    }

    /// Get parsed `KeySequence`s for an action.
    pub fn get_key_sequences(&self, action: &str) -> Vec<KeySequence> {
        self.get_keys(action)
            .iter()
            .filter_map(|k| KeySequence::parse(k))
            .collect()
    }

    /// Check if a key sequence matches an action.
    pub fn matches(&self, action: &str, seq: &KeySequence) -> bool {
        self.get_key_sequences(action).iter().any(|k| k == seq)
    }

    /// Look up which action matches the given key sequence.
    pub fn action_for(&self, seq: &KeySequence) -> Option<String> {
        for action in self.bindings.keys() {
            if self.matches(action, seq) {
                return Some(action.clone());
            }
        }
        None
    }

    /// Get a binding definition by action name.
    pub fn get_binding(&self, action: &str) -> Option<&KeyBinding> {
        self.bindings.get(action)
    }

    /// Get all registered bindings.
    pub fn all_bindings(&self) -> &HashMap<String, KeyBinding> {
        &self.bindings
    }

    /// Get user overrides.
    pub fn user_overrides(&self) -> &HashMap<String, Vec<String>> {
        &self.user_overrides
    }

    /// Get project overrides.
    pub fn project_overrides(&self) -> &HashMap<String, Vec<String>> {
        &self.project_overrides
    }

    /// Get detected conflicts.
    pub fn conflicts(&self) -> &[KeybindingConflict] {
        &self.conflicts
    }

    /// Check whether there are any conflicts.
    pub fn has_conflicts(&self) -> bool {
        !self.conflicts.is_empty()
    }

    /// Reload overrides from files.
    pub fn reload(&mut self, user_path: Option<&Path>, project_path: Option<&Path>) {
        self.user_overrides.clear();
        self.project_overrides.clear();

        if let Some(path) = user_path {
            if let Ok(keys) = Self::load_toml_overrides(path) {
                self.user_overrides = keys;
            } else if let Ok(keys) = Self::load_json_overrides(path) {
                self.user_overrides = keys;
            }
        }
        if let Some(path) = project_path {
            if let Ok(keys) = Self::load_toml_overrides(path) {
                self.project_overrides = keys;
            }
        }
        self.detect_conflicts();
    }

    /// Export current effective overrides to a TOML file.
    pub fn export_to_toml(&self, path: &Path) -> std::io::Result<()> {
        let mut keybindings = HashMap::new();
        // Export only user-level overrides, not project overrides
        for (action, keys) in &self.user_overrides {
            if keys.len() == 1 {
                keybindings.insert(
                    action.clone(),
                    TomlKeyBinding::Single(keys[0].clone()),
                );
            } else {
                keybindings.insert(
                    action.clone(),
                    TomlKeyBinding::Multiple(keys.clone()),
                );
            }
        }
        let file = KeybindingsFile { keybindings };
        let content = toml::to_string_pretty(&file)
            .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
        std::fs::write(path, content)
    }

    // -- Private helpers --

    /// Detect conflicts where the same key is bound to multiple actions.
    fn detect_conflicts(&mut self) {
        self.conflicts.clear();
        let mut key_to_actions: HashMap<String, Vec<String>> = HashMap::new();

        for action in self.bindings.keys() {
            for key in self.get_keys(action) {
                key_to_actions
                    .entry(key)
                    .or_default()
                    .push(action.clone());
            }
        }

        for (key, actions) in key_to_actions {
            if actions.len() > 1 {
                // Sort for deterministic output
                let mut sorted = actions;
                sorted.sort();
                self.conflicts.push(KeybindingConflict {
                    key,
                    actions: sorted,
                });
            }
        }

        // Sort conflicts by key for deterministic output
        self.conflicts.sort_by(|a, b| a.key.cmp(&b.key));
    }

    /// Load overrides from a TOML file.
    fn load_toml_overrides(path: &Path) -> Result<HashMap<String, Vec<String>>, String> {
        let content =
            std::fs::read_to_string(path).map_err(|e| format!("IO error: {}", e))?;
        let file: KeybindingsFile =
            toml::from_str(&content).map_err(|e| format!("TOML parse error: {}", e))?;
        let mut overrides = HashMap::new();
        for (action, binding) in file.keybindings {
            let keys = binding.to_vec();
            // Validate each key can be parsed
            for key in &keys {
                if KeySequence::parse(key).is_none() {
                    tracing::warn!(
                        "Ignoring unparseable key '{}' for action '{}'",
                        key,
                        action
                    );
                }
            }
            overrides.insert(action, keys);
        }
        Ok(overrides)
    }

    /// Load overrides from a legacy JSON file.
    fn load_json_overrides(path: &Path) -> Result<HashMap<String, Vec<String>>, String> {
        let content =
            std::fs::read_to_string(path).map_err(|e| format!("IO error: {}", e))?;
        let parsed: serde_json::Value =
            serde_json::from_str(&content).map_err(|e| format!("JSON parse error: {}", e))?;
        let mut overrides = HashMap::new();
        if let serde_json::Value::Object(map) = parsed {
            for (action, value) in map {
                let keys = match value {
                    serde_json::Value::String(s) => vec![s],
                    serde_json::Value::Array(arr) => arr
                        .iter()
                        .filter_map(|v| v.as_str().map(String::from))
                        .collect(),
                    _ => continue,
                };
                overrides.insert(action, keys);
            }
        }
        Ok(overrides)
    }
}

impl Default for KeybindingsManager {
    fn default() -> Self {
        Self::new()
    }
}

// ---------------------------------------------------------------------------
// Path helpers
// ---------------------------------------------------------------------------

/// Get the default user keybindings file path (`~/.oxi/keybindings.toml`).
pub fn default_keybindings_path() -> Option<PathBuf> {
    dirs::home_dir().map(|p| p.join(".oxi").join("keybindings.toml"))
}

/// Get the project keybindings file path (`.oxi/keybindings.toml`).
pub fn project_keybindings_path(project_dir: &Path) -> PathBuf {
    project_dir.join(".oxi").join("keybindings.toml")
}

// ---------------------------------------------------------------------------
// Legacy compatibility (preserving public API)
// ---------------------------------------------------------------------------

/// Load user keybindings from a JSON file (legacy).
pub fn load_user_keybindings(path: &Path) -> Option<UserKeybindings> {
    let content = std::fs::read_to_string(path).ok()?;
    serde_json::from_str(&content).ok()
}

/// Save user keybindings to a JSON file (legacy).
pub fn save_user_keybindings(path: &Path, bindings: &UserKeybindings) -> std::io::Result<()> {
    let content = serde_json::to_string_pretty(bindings)
        .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
    std::fs::write(path, content)
}

/// User-configurable keybindings (legacy JSON format).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserKeybindings {
    /// Map of action name to custom key sequence(s)
    #[serde(default)]
    pub bindings: HashMap<String, Vec<String>>,
}

/// Parse a key sequence string into components (legacy).
/// Returns `Vec<(ctrl, alt, key_char)>`.
pub fn parse_key_sequence(sequence: &str) -> Vec<(bool, bool, char)> {
    let mut result = Vec::new();
    let parts: Vec<&str> = sequence.split('+').collect();

    let mut ctrl = false;
    let mut alt = false;

    for part in parts {
        let part_lower = part.to_lowercase();

        if part_lower == "ctrl" || part_lower == "control" {
            ctrl = true;
            continue;
        }
        if part_lower == "alt" || part_lower == "meta" {
            alt = true;
            continue;
        }

        let key_char = match part_lower.as_str() {
            "space" => ' ',
            "tab" => '\t',
            "enter" | "return" => '\n',
            "escape" | "esc" => '\x1b',
            "backspace" => '\x7f',
            _ => part.chars().next().unwrap_or(' '),
        };

        result.push((ctrl, alt, key_char));
        ctrl = false;
        alt = false;
    }

    result
}

/// Format key sequences for display (legacy).
pub fn format_key_sequence(keys: &[String]) -> String {
    keys.iter()
        .map(|k| {
            let parts: Vec<&str> = k.split('+').collect();
            let mut formatted_parts = Vec::new();
            for (i, part) in parts.iter().enumerate() {
                let lower = part.to_lowercase();
                if lower == "ctrl" {
                    formatted_parts.push("Ctrl".to_string());
                } else if lower == "alt" || lower == "meta" {
                    formatted_parts.push("Alt".to_string());
                } else if lower == "shift" {
                    formatted_parts.push("Shift".to_string());
                } else if i == parts.len() - 1 {
                    let c = part.chars().next().unwrap_or(' ');
                    formatted_parts.push(c.to_uppercase().collect::<String>());
                } else {
                    formatted_parts.push(part.to_string());
                }
            }
            formatted_parts.join("+")
        })
        .collect::<Vec<_>>()
        .join(", ")
}

// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------

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

    // --- KeySequence parsing ---

    #[test]
    fn test_parse_ctrl_c() {
        let seq = KeySequence::parse("ctrl+c").unwrap();
        assert!(seq.ctrl);
        assert!(!seq.alt);
        assert!(!seq.shift);
        assert_eq!(seq.key, KeyName::Char('c'));
    }

    #[test]
    fn test_parse_alt_enter() {
        let seq = KeySequence::parse("alt+enter").unwrap();
        assert!(!seq.ctrl);
        assert!(seq.alt);
        assert_eq!(seq.key, KeyName::Enter);
    }

    #[test]
    fn test_parse_shift_tab() {
        let seq = KeySequence::parse("shift+tab").unwrap();
        assert!(seq.shift);
        assert_eq!(seq.key, KeyName::Tab);
    }

    #[test]
    fn test_parse_ctrl_shift_p() {
        let seq = KeySequence::parse("ctrl+shift+p").unwrap();
        assert!(seq.ctrl);
        assert!(seq.shift);
        assert_eq!(seq.key, KeyName::Char('p'));
    }

    #[test]
    fn test_parse_shift_ctrl_p() {
        // Modifier order should not matter
        let seq = KeySequence::parse("shift+ctrl+p").unwrap();
        assert!(seq.ctrl);
        assert!(seq.shift);
        assert_eq!(seq.key, KeyName::Char('p'));
    }

    #[test]
    fn test_parse_escape() {
        let seq = KeySequence::parse("escape").unwrap();
        assert_eq!(seq.key, KeyName::Escape);
        assert!(!seq.ctrl && !seq.alt && !seq.shift);
    }

    #[test]
    fn test_parse_esc() {
        let seq = KeySequence::parse("esc").unwrap();
        assert_eq!(seq.key, KeyName::Escape);
    }

    #[test]
    fn test_parse_f1_through_f12() {
        for n in 1..=12 {
            let key = format!("f{}", n);
            let seq = KeySequence::parse(&key).unwrap();
            assert_eq!(seq.key, KeyName::F(n), "Failed for {}", key);
        }
    }

    #[test]
    fn test_parse_f0_invalid() {
        assert!(KeySequence::parse("f0").is_none());
    }

    #[test]
    fn test_parse_f13_invalid() {
        assert!(KeySequence::parse("f13").is_none());
    }

    #[test]
    fn test_parse_pageup() {
        let seq = KeySequence::parse("pageup").unwrap();
        assert_eq!(seq.key, KeyName::PageUp);
    }

    #[test]
    fn test_parse_pagedown() {
        let seq = KeySequence::parse("pagedown").unwrap();
        assert_eq!(seq.key, KeyName::PageDown);
    }

    #[test]
    fn test_parse_home() {
        let seq = KeySequence::parse("home").unwrap();
        assert_eq!(seq.key, KeyName::Home);
    }

    #[test]
    fn test_parse_end() {
        let seq = KeySequence::parse("end").unwrap();
        assert_eq!(seq.key, KeyName::End);
    }

    #[test]
    fn test_parse_delete() {
        let seq = KeySequence::parse("delete").unwrap();
        assert_eq!(seq.key, KeyName::Delete);
    }

    #[test]
    fn test_parse_backspace() {
        let seq = KeySequence::parse("backspace").unwrap();
        assert_eq!(seq.key, KeyName::Backspace);
    }

    #[test]
    fn test_parse_insert() {
        let seq = KeySequence::parse("insert").unwrap();
        assert_eq!(seq.key, KeyName::Insert);
    }

    #[test]
    fn test_parse_ctrl_alt_delete() {
        let seq = KeySequence::parse("ctrl+alt+delete").unwrap();
        assert!(seq.ctrl);
        assert!(seq.alt);
        assert_eq!(seq.key, KeyName::Delete);
    }

    #[test]
    fn test_parse_invalid() {
        assert!(KeySequence::parse("").is_none());
        assert!(KeySequence::parse("   ").is_none());
        assert!(KeySequence::parse("foobar").is_none());
    }

    #[test]
    fn test_parse_single_char() {
        let seq = KeySequence::parse("a").unwrap();
        assert_eq!(seq.key, KeyName::Char('a'));
    }

    // --- KeySequence display ---

    #[test]
    fn test_key_sequence_display() {
        let seq = KeySequence::parse("ctrl+alt+delete").unwrap();
        assert_eq!(seq.to_string(), "ctrl+alt+delete");
    }

    #[test]
    fn test_key_name_display() {
        assert_eq!(KeyName::Enter.to_string(), "enter");
        assert_eq!(KeyName::F(5).to_string(), "f5");
        assert_eq!(KeyName::PageUp.to_string(), "pageup");
    }

    // --- Round-trip notation ---

    #[test]
    fn test_notation_round_trip() {
        let notations = vec![
            "ctrl+c", "alt+enter", "shift+tab", "escape", "enter", "tab",
            "ctrl+shift+p", "alt+backspace", "f1", "f12", "pageup", "pagedown",
            "home", "end", "delete", "backspace",
        ];
        for notation in notations {
            let seq = KeySequence::parse(notation).unwrap_or_else(|| {
                panic!("Failed to parse: {}", notation)
            });
            let round_tripped = seq.to_notation();
            let re_parsed = KeySequence::parse(&round_tripped).unwrap_or_else(|| {
                panic!("Failed to re-parse: {}", round_tripped)
            });
            assert_eq!(seq, re_parsed, "Round-trip failed for {}", notation);
        }
    }

    // --- KeyAction ---

    #[test]
    fn test_key_action_from_str() {
        assert_eq!(KeyAction::from("app.interrupt"), KeyAction::Interrupt);
        assert_eq!(KeyAction::from("app.clear"), KeyAction::Clear);
        assert_eq!(KeyAction::from("submit"), KeyAction::Submit);
        assert_eq!(
            KeyAction::from("tui.editor.cursorUp"),
            KeyAction::CursorUp
        );
        assert_eq!(
            KeyAction::from("tui.input.submit"),
            KeyAction::Submit
        );
    }

    #[test]
    fn test_key_action_custom() {
        let action = KeyAction::from("my-extension.action");
        assert!(matches!(action, KeyAction::Custom(s) if s == "my-extension.action"));
    }

    // --- Default keybindings ---

    #[test]
    fn test_default_keybindings_not_empty() {
        let bindings = default_keybindings();
        assert!(!bindings.is_empty());
        // Verify key actions exist
        assert!(bindings.contains_key("app.interrupt"));
        assert!(bindings.contains_key("tui.input.submit"));
        assert!(bindings.contains_key("tui.editor.cursorUp"));
        assert!(bindings.contains_key("app.thinking.cycle"));
        assert!(bindings.contains_key("app.tree.filter.cycleForward"));
    }

    // --- KeybindingsManager basics ---

    #[test]
    fn test_keybindings_manager_new() {
        let manager = KeybindingsManager::new();
        assert!(!manager.all_bindings().is_empty());
    }

    #[test]
    fn test_get_keys_default() {
        let manager = KeybindingsManager::new();
        let keys = manager.get_keys("app.interrupt");
        assert_eq!(keys, vec!["escape"]);
    }

    #[test]
    fn test_set_user_override() {
        let mut manager = KeybindingsManager::new();
        manager.set_user_override("app.interrupt", vec!["ctrl+c".to_string()]);
        assert_eq!(
            manager.get_keys("app.interrupt"),
            vec!["ctrl+c"]
        );
    }

    #[test]
    fn test_project_override_takes_priority() {
        let mut manager = KeybindingsManager::new();
        manager.set_user_override("app.interrupt", vec!["ctrl+c".to_string()]);
        manager.set_project_override("app.interrupt", vec!["f1".to_string()]);
        // Project overrides user
        assert_eq!(manager.get_keys("app.interrupt"), vec!["f1"]);
    }

    #[test]
    fn test_matches() {
        let manager = KeybindingsManager::new();
        assert!(manager.matches("app.interrupt", &KeySequence::parse("escape").unwrap()));
        assert!(manager.matches("app.exit", &KeySequence::parse("ctrl+d").unwrap()));
    }

    #[test]
    fn test_action_for() {
        let manager = KeybindingsManager::new();
        assert_eq!(
            manager.action_for(&KeySequence::parse("ctrl+d").unwrap()),
            Some("app.exit".to_string())
        );
    }

    #[test]
    fn test_get_key_sequences() {
        let manager = KeybindingsManager::new();
        let seqs = manager.get_key_sequences("tui.editor.cursorLeft");
        assert!(!seqs.is_empty());
        assert!(seqs.iter().any(|s| s.key == KeyName::Left));
        assert!(seqs.iter().any(|s| s.ctrl && s.key == KeyName::Char('b')));
    }

    // --- Conflict detection ---

    #[test]
    fn test_conflict_detection_on_create() {
        let manager = KeybindingsManager::new();
        // Many default bindings share keys (e.g. ctrl+c used in multiple places)
        // so conflicts should be detected
        if !manager.conflicts().is_empty() {
            // Verify conflict structure is valid
            for conflict in manager.conflicts() {
                assert!(!conflict.key.is_empty());
                assert!(conflict.actions.len() > 1);
            }
        }
    }

    #[test]
    fn test_conflict_after_override() {
        let mut manager = KeybindingsManager::new();
        // Create a conflict: bind two actions to the same key
        manager.set_user_override("app.interrupt", vec!["ctrl+x".to_string()]);
        manager.set_user_override("app.clear", vec!["ctrl+x".to_string()]);
        let conflicts: Vec<_> = manager
            .conflicts()
            .iter()
            .filter(|c| c.key == "ctrl+x")
            .collect();
        assert_eq!(conflicts.len(), 1);
        assert!(conflicts[0].actions.contains(&"app.interrupt".to_string()));
        assert!(conflicts[0].actions.contains(&"app.clear".to_string()));
    }

    #[test]
    fn test_no_conflict_different_keys() {
        let mut manager = KeybindingsManager::new();
        manager.set_user_override("app.interrupt", vec!["f1".to_string()]);
        manager.set_user_override("app.clear", vec!["f2".to_string()]);
        let conflicts: Vec<_> = manager
            .conflicts()
            .iter()
            .filter(|c| c.key == "f1" || c.key == "f2")
            .collect();
        assert!(conflicts.is_empty());
    }

    // --- TOML loading ---

    #[test]
    fn test_load_toml_overrides() {
        let dir = tempfile::tempdir().unwrap();
        let path = dir.path().join("keybindings.toml");
        let mut f = std::fs::File::create(&path).unwrap();
        writeln!(
            f,
            r#"
[keybindings]
"app.interrupt" = "f1"
"tui.editor.cursorUp" = ["up", "k"]
"app.exit" = "ctrl+d"
"#
        )
        .unwrap();

        let overrides = KeybindingsManager::load_toml_overrides(&path).unwrap();
        assert_eq!(overrides.get("app.interrupt"), Some(&vec!["f1".to_string()]));
        assert_eq!(
            overrides.get("tui.editor.cursorUp"),
            Some(&vec!["up".to_string(), "k".to_string()])
        );
    }

    #[test]
    fn test_load_toml_invalid_key_warns() {
        let dir = tempfile::tempdir().unwrap();
        let path = dir.path().join("keybindings.toml");
        let mut f = std::fs::File::create(&path).unwrap();
        writeln!(
            f,
            r#"
[keybindings]
"app.interrupt" = "!!!invalid!!!"
"#
        )
        .unwrap();

        // Should load but warn (tracing), not panic
        let overrides = KeybindingsManager::load_toml_overrides(&path).unwrap();
        assert!(overrides.contains_key("app.interrupt"));
    }

    #[test]
    fn test_load_json_overrides() {
        let dir = tempfile::tempdir().unwrap();
        let path = dir.path().join("keybindings.json");
        let mut f = std::fs::File::create(&path).unwrap();
        writeln!(
            f,
            r#"{{"app.interrupt": "f1", "app.clear": ["ctrl+c", "escape"]}}"#
        )
        .unwrap();

        let overrides = KeybindingsManager::load_json_overrides(&path).unwrap();
        assert_eq!(overrides.get("app.interrupt"), Some(&vec!["f1".to_string()]));
        assert_eq!(
            overrides.get("app.clear"),
            Some(&vec!["ctrl+c".to_string(), "escape".to_string()])
        );
    }

    // --- from_paths ---

    #[test]
    fn test_from_paths_toml() {
        let dir = tempfile::tempdir().unwrap();
        let path = dir.path().join("keybindings.toml");
        let mut f = std::fs::File::create(&path).unwrap();
        writeln!(
            f,
            r#"
[keybindings]
"app.interrupt" = "f1"
"#
        )
        .unwrap();

        let manager = KeybindingsManager::from_paths(Some(&path), None);
        assert_eq!(manager.get_keys("app.interrupt"), vec!["f1"]);
    }

    #[test]
    fn test_from_paths_project_overrides_user() {
        let dir = tempfile::tempdir().unwrap();
        let user_path = dir.path().join("user.toml");
        let project_path = dir.path().join("project.toml");

        let mut f1 = std::fs::File::create(&user_path).unwrap();
        writeln!(f1, "[keybindings]\n\"app.interrupt\" = \"f1\"").unwrap();

        let mut f2 = std::fs::File::create(&project_path).unwrap();
        writeln!(f2, "[keybindings]\n\"app.interrupt\" = \"f2\"").unwrap();

        let manager = KeybindingsManager::from_paths(Some(&user_path), Some(&project_path));
        assert_eq!(manager.get_keys("app.interrupt"), vec!["f2"]);
    }

    // --- Export TOML ---

    #[test]
    fn test_export_to_toml() {
        let dir = tempfile::tempdir().unwrap();
        let path = dir.path().join("export.toml");

        let mut manager = KeybindingsManager::new();
        manager.set_user_override("app.interrupt", vec!["f1".to_string()]);
        manager.set_user_override(
            "tui.editor.cursorUp",
            vec!["up".to_string(), "k".to_string()],
        );

        manager.export_to_toml(&path).unwrap();

        // Re-read and verify
        let content = std::fs::read_to_string(&path).unwrap();
        assert!(content.contains("app.interrupt"));
        assert!(content.contains("f1"));

        let parsed: KeybindingsFile = toml::from_str(&content).unwrap();
        assert_eq!(
            parsed.keybindings.get("app.interrupt"),
            Some(&TomlKeyBinding::Single("f1".to_string()))
        );
    }

    // --- TOML serde ---

    #[test]
    fn test_toml_key_binding_serde() {
        let single = TomlKeyBinding::Single("ctrl+c".to_string());
        let json = serde_json::to_string(&single).unwrap();
        let parsed: TomlKeyBinding = serde_json::from_str(&json).unwrap();
        assert_eq!(parsed, single);

        let multi = TomlKeyBinding::Multiple(vec!["a".to_string(), "b".to_string()]);
        let json = serde_json::to_string(&multi).unwrap();
        let parsed: TomlKeyBinding = serde_json::from_str(&json).unwrap();
        assert_eq!(parsed, multi);
    }

    #[test]
    fn test_toml_key_binding_to_vec() {
        let single = TomlKeyBinding::Single("ctrl+c".to_string());
        assert_eq!(single.to_vec(), vec!["ctrl+c"]);

        let multi = TomlKeyBinding::Multiple(vec!["a".to_string(), "b".to_string()]);
        assert_eq!(multi.to_vec(), vec!["a", "b"]);
    }

    // --- Legacy API ---

    #[test]
    fn test_user_keybindings_serde() {
        let user = UserKeybindings {
            bindings: HashMap::from([("app.interrupt".to_string(), vec!["ctrl+c".to_string()])]),
        };
        let json = serde_json::to_string(&user).unwrap();
        let parsed: UserKeybindings = serde_json::from_str(&json).unwrap();
        assert_eq!(
            parsed.bindings.get("app.interrupt"),
            Some(&vec!["ctrl+c".to_string()])
        );
    }

    #[test]
    fn test_keybinding_struct() {
        let binding = KeyBinding::new("test.action", vec!["a", "b"], "Test action");
        assert_eq!(binding.action, "test.action");
        assert_eq!(binding.default_keys, vec!["a", "b"]);
        assert_eq!(binding.description, "Test action");
    }

    #[test]
    fn test_parse_key_sequence_legacy() {
        let result = parse_key_sequence("ctrl+c");
        assert!(result.iter().any(|(ctrl, _, _)| *ctrl));

        let result = parse_key_sequence("alt+x");
        assert!(result.iter().any(|(_, alt, _)| *alt));
    }

    #[test]
    fn test_format_key_sequence_legacy() {
        let keys = vec!["ctrl+c".to_string(), "ctrl+x".to_string()];
        let formatted = format_key_sequence(&keys);
        assert!(formatted.contains("Ctrl+C"));
        assert!(formatted.contains("Ctrl+X"));
    }

    #[test]
    fn test_default_keybindings_path() {
        let path = default_keybindings_path();
        assert!(path.is_some());
        let p = path.unwrap();
        assert!(p.to_string_lossy().contains(".oxi"));
        assert!(p.to_string_lossy().contains("keybindings.toml"));
    }

    #[test]
    fn test_project_keybindings_path() {
        let p = project_keybindings_path(Path::new("/my/project"));
        assert_eq!(p, PathBuf::from("/my/project/.oxi/keybindings.toml"));
    }
}
