from __future__ import annotations

import json
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from database import Database

DEFAULT_BINDINGS = {
    "global": {
        "toggle_select": "ctrl+l",
        "exit": "escape",
    },
    "find_mode": {
        "find_next": "enter",
        "find_prev": "shift+enter",
        "exit": "escape",
    },
    "select_mode": {
        "select_all": "ctrl+a",
        "delete": "delete",
        "clear_selection": "c",
        "copy": "ctrl+c",
        "exit": "escape",
    },
    "composer": {
        "select_all": "ctrl+a",
        "copy": "ctrl+c",
        "paste": "ctrl+v",
        "cut": "ctrl+x",
        "select_left": "shift+left",
        "select_right": "shift+right",
        "select_word_left": "ctrl+shift+left",
        "select_word_right": "ctrl+shift+right",
        "move_word_left": "ctrl+left",
        "move_word_right": "ctrl+right",
        "history_prev": "ctrl+up",
        "history_next": "ctrl+down",
        "send": "enter",
        "newline": "ctrl+j",
    },
    "navigation": {
        "scroll_up": "up",
        "scroll_down": "down",
        "scroll_page_up": "pageup",
        "scroll_page_down": "pagedown",
        "scroll_home": "home",
        "scroll_end": "end",
    },
}

ACTION_I18N_MAP = {
    "toggle_select": "bind_toggle_select",
    "exit": "bind_exit",
    "find_next": "bind_find_next",
    "find_prev": "bind_find_prev",
    "select_all": "bind_select_all",
    "delete": "bind_delete",
    "clear_selection": "bind_clear_selection",
    "copy": "bind_copy",
    "paste": "bind_paste",
    "cut": "bind_cut",
    "select_left": "bind_select_left",
    "select_right": "bind_select_right",
    "select_word_left": "bind_select_word_left",
    "select_word_right": "bind_select_word_right",
    "move_word_left": "bind_move_word_left",
    "move_word_right": "bind_move_word_right",
    "history_prev": "bind_history_prev",
    "history_next": "bind_history_next",
    "send": "bind_send",
    "newline": "bind_newline",
    "scroll_up": "bind_scroll_up",
    "scroll_down": "bind_scroll_down",
    "scroll_page_up": "bind_scroll_page_up",
    "scroll_page_down": "bind_scroll_page_down",
    "scroll_home": "bind_scroll_home",
    "scroll_end": "bind_scroll_end",
}

CONTEXT_I18N_MAP = {
    "global": "bindings_global",
    "find_mode": "bindings_find_mode",
    "select_mode": "bindings_select_mode",
    "composer": "bindings_composer",
    "navigation": "bindings_navigation",
}


def normalize_key(key: str) -> str:
    return key.lower().strip()


def format_key_display(key: str) -> str:
    parts = key.split("+")
    formatted = []
    for p in parts:
        p = p.strip()
        if p.lower() in ("ctrl", "shift", "alt", "meta", "cmd"):
            formatted.append(p.capitalize())
        elif p.lower() == "escape":
            formatted.append("Esc")
        elif p.lower() == "enter":
            formatted.append("Enter")
        elif p.lower() == "delete":
            formatted.append("Delete")
        elif p.lower() in ("pageup", "page_up"):
            formatted.append("PageUp")
        elif p.lower() in ("pagedown", "page_down"):
            formatted.append("PageDown")
        elif p.lower() == "home":
            formatted.append("Home")
        elif p.lower() == "end":
            formatted.append("End")
        elif p.lower() == "up":
            formatted.append("Up")
        elif p.lower() == "down":
            formatted.append("Down")
        elif p.lower() == "left":
            formatted.append("Left")
        elif p.lower() == "right":
            formatted.append("Right")
        elif p.lower() == "space":
            formatted.append("Space")
        elif p.lower() == "tab":
            formatted.append("Tab")
        elif p.lower() == "backspace":
            formatted.append("Backspace")
        else:
            formatted.append(p.upper())
    return "+".join(formatted)


def parse_textual_key(event) -> str:
    key = event.key

    return normalize_key(key)


class KeybindingsManager:

    def __init__(self, db: "Database" = None):
        self.db = db
        self._bindings: dict[str, dict[str, str]] = {}
        self._load()

    def _load(self) -> None:
        self._bindings = {}
        for ctx, actions in DEFAULT_BINDINGS.items():
            self._bindings[ctx] = dict(actions)

        if self.db:
            try:
                stored = self.db.get_setting("keybindings")
                if stored:
                    custom = json.loads(stored)
                    for ctx, actions in custom.items():
                        if ctx in self._bindings:
                            self._bindings[ctx].update(actions)
            except Exception:
                pass

    def _save(self) -> None:
        if not self.db:
            return

        custom = {}
        for ctx, actions in self._bindings.items():
            defaults = DEFAULT_BINDINGS.get(ctx, {})
            diff = {}
            for action, key in actions.items():
                if action not in defaults or defaults[action] != key:
                    diff[action] = key
            if diff:
                custom[ctx] = diff

        try:
            self.db.set_setting("keybindings", json.dumps(custom))
        except Exception:
            pass

    def get(self, context: str, action: str) -> str:
        return self._bindings.get(context, {}).get(action, "")

    def get_display(self, context: str, action: str) -> str:
        key = self.get(context, action)
        return format_key_display(key) if key else ""

    def set(self, context: str, action: str, key: str) -> None:
        if context not in self._bindings:
            self._bindings[context] = {}
        self._bindings[context][action] = normalize_key(key)
        self._save()

    def reset(self, context: str = None, action: str = None) -> None:
        if context is None:
            self._bindings = {}
            for ctx, actions in DEFAULT_BINDINGS.items():
                self._bindings[ctx] = dict(actions)
        elif action is None:
            if context in DEFAULT_BINDINGS:
                self._bindings[context] = dict(DEFAULT_BINDINGS[context])
        else:
            if context in DEFAULT_BINDINGS and action in DEFAULT_BINDINGS[context]:
                self._bindings[context][action] = DEFAULT_BINDINGS[context][action]
        self._save()

    def matches(self, context: str, action: str, event) -> bool:
        bound_key = self.get(context, action)
        if not bound_key:
            return False

        event_key = parse_textual_key(event)
        return normalize_key(bound_key) == normalize_key(event_key)

    def get_all(self) -> dict[str, dict[str, str]]:
        return self._bindings

    def find_conflicts(self, context: str, new_key: str, exclude_action: str = None) -> list[tuple[str, str]]:
        conflicts = []
        new_key_norm = normalize_key(new_key)

        for action, key in self._bindings.get(context, {}).items():
            if action != exclude_action and normalize_key(key) == new_key_norm:
                conflicts.append((context, action))

        return conflicts


_manager: KeybindingsManager | None = None


def get_keybindings_manager() -> KeybindingsManager | None:
    return _manager


def init_keybindings_manager(db: "Database") -> KeybindingsManager:
    global _manager
    _manager = KeybindingsManager(db)
    return _manager
