from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtGui import QKeySequence
from qgis.PyQt.QtWidgets import QAction, QShortcut
from qgis.core import QgsProject


class BuiltinHotkeyManager:
    """管理插件内置快捷键的应用与还原。

    还原策略：在 activate() 中、真正修改每个 action 之前，即时抓取其当前
    真实快捷键，用列表持有 QAction 引用（避免 PyQt 中 QAction 作为字典键
    身份不稳定的问题）。deactivate() 时把这些 action 精确还原到抓取时的
    快捷键，实时生效、无需重启。
    """

    LAYER_SWITCH_KEYS = ["Alt+1", "Alt+2", "Alt+3", "Alt+4", "Alt+5",
                         "Alt+6", "Alt+7", "Alt+8", "Alt+9", "Alt+0"]

    def __init__(self, iface):
        self.iface = iface
        self._original = []  # list[(QAction, [QKeySequence, ...])]
        self._extra_shortcuts = []
        self._active = False
        # (组合键, 解析器)，解析器返回目标 QAction 或 None。
        self._managed_bindings = [
            ("E", self._iface_action("actionToggleEditing")),
            ("S", self._iface_action("actionSelect")),
            ("V", self._iface_action("actionVertexToolActiveLayer")),
            ("M", self._iface_action("actionMoveFeature")),
            ("R", self._object_action("mActionRotateFeature", ["Rotate Feature(s)"])),
            ("O", self._object_action("mActionOffsetCurve", ["Offset Curve"])),
            ("P", self._iface_action("actionSplitFeatures")),
            ("N", self._snapping_action),
            ("G", self._object_action("mActionMergeFeatures", ["Merge Selected Features"])),
            ("F", self._object_action("mActionFillRing", ["Fill Ring"])),
            ("C", self._iface_action("actionAddFeature")),
            ("Ctrl+S", self._iface_action("actionSaveAllEdits")),
        ]

    def record_baseline(self):
        """保留此接口以兼容调用方。

        原始快捷键不再于插件加载期快照——加载期读取到的快捷键可能尚未
        由 QGIS 完全应用/可能不准，且会“抢先占位”导致激活时读到的真实值
        被去重逻辑跳过，最终还原成错误值。改为在 activate() 中，真正修改
        每个 action 之前即时抓取当前真实快捷键，确保还原绝对准确、实时生效。
        """
        return

    def activate(self):
        if self._active:
            return
        self._clear_extra_shortcuts()
        self._original = []  # 每次激活重新即时快照当前真实状态

        # 1. 先把我们要用的组合键从所有 action 上摘除，避免快捷键歧义。
        #    _clear_sequence 会在改动前即时记录被摘除 action 的原始快捷键。
        for key_text, _resolver in self._managed_bindings:
            self._clear_sequence(key_text)
        for key_text in self.LAYER_SWITCH_KEYS:
            self._clear_sequence(key_text)

        # 2. 把组合键赋给目标 action；无原生 action 的（如捕捉）用自定义 QShortcut。
        #    覆写前即时记录目标 action 的原始快捷键。
        for key_text, resolver in self._managed_bindings:
            action = resolver()
            if action is not None:
                self._remember(action)
                action.setShortcut(QKeySequence(key_text))
            elif key_text == "N":
                self._install_custom_shortcut(key_text, self._toggle_snapping)

        # 3. Alt+1..0 图层切换（无原生 action，用自定义 QShortcut）。
        self._install_layer_switch_shortcuts()
        self._active = True

    def deactivate(self):
        if self._active:
            # 逆序还原，保证同一 action 若被多次触碰也回到最早的原始值。
            for action, shortcuts in reversed(self._original):
                try:
                    action.setShortcuts(shortcuts)
                except RuntimeError:
                    pass  # 底层 C++ 对象已销毁时安全忽略
        self._clear_extra_shortcuts()
        self._original = []
        self._active = False

    def _iface_action(self, method_name):
        def resolver():
            getter = getattr(self.iface, method_name, None)
            if callable(getter):
                return getter()
            return None

        return resolver

    def _object_action(self, object_name, fallback_texts):
        def resolver():
            action = self.iface.mainWindow().findChild(QAction, object_name)
            if action:
                return action

            normalized_targets = {self._normalize_action_text(text) for text in fallback_texts}
            for candidate in self.iface.mainWindow().findChildren(QAction):
                if self._normalize_action_text(candidate.text()) in normalized_targets:
                    return candidate
            return None

        return resolver

    def _snapping_action(self):
        return self.iface.mainWindow().findChild(QAction, "EnableSnappingAction")

    def _install_layer_switch_shortcuts(self):
        for index, key_text in enumerate(self.LAYER_SWITCH_KEYS):
            self._install_custom_shortcut(
                key_text,
                lambda idx=index: self._activate_layer_by_index(idx)
            )

    def _install_custom_shortcut(self, key_text, callback):
        shortcut = QShortcut(QKeySequence(key_text), self.iface.mainWindow())
        context = getattr(Qt, "WindowShortcut", None)
        if context is None:
            context = Qt.ShortcutContext.WindowShortcut
        shortcut.setContext(context)
        shortcut.activated.connect(callback)
        self._extra_shortcuts.append(shortcut)

    def _clear_extra_shortcuts(self):
        for shortcut in self._extra_shortcuts:
            shortcut.setEnabled(False)
            shortcut.deleteLater()
        self._extra_shortcuts.clear()

    def _toggle_snapping(self):
        snapping_utils = self.iface.mapCanvas().snappingUtils()
        if snapping_utils:
            snapping_utils.toggleEnabled()

    def _activate_layer_by_index(self, index):
        layer_tree_layers = QgsProject.instance().layerTreeRoot().findLayers()
        if index >= len(layer_tree_layers):
            return

        layer = layer_tree_layers[index].layer()
        if not layer:
            return

        self.iface.setActiveLayer(layer)
        layer_tree_view = self.iface.layerTreeView()
        if layer_tree_view:
            layer_tree_view.setCurrentLayer(layer)

    def _clear_sequence(self, key_text):
        target_text = self._seq_text(QKeySequence(key_text))
        for action in self.iface.mainWindow().findChildren(QAction):
            current = action.shortcuts()
            kept = [s for s in current if self._seq_text(s) != target_text]
            if len(kept) != len(current):
                self._remember(action)  # 变更前确保原始值已入快照
                action.setShortcuts(kept)

    def _remember(self, action):
        for existing, _shortcuts in self._original:
            if existing is action or existing == action:
                return
        self._original.append((action, list(action.shortcuts())))

    def _seq_text(self, shortcut):
        portable = getattr(QKeySequence, "PortableText", None)
        if portable is not None:
            return QKeySequence(shortcut).toString(portable)
        return QKeySequence(shortcut).toString()

    def _normalize_action_text(self, text):
        return text.replace("&", "").replace("...", "").strip().lower()
