"""Prompts management window and quick-select popup — PyQt6.

PromptsWindow:    Full CRUD window for managing prompt templates (QTreeWidget by category).
EditPromptDialog: Modal dialog for creating / editing a prompt.
QuickSelectDialog: Compact popup shown on the prompts hotkey (slash mode supported).
"""

from __future__ import annotations

import logging
import re
from collections.abc import Callable

from PyQt6.QtCore import Qt, QTimer
from PyQt6.QtGui import QFont
from PyQt6.QtWidgets import (
    QAbstractItemView,
    QComboBox,
    QDialog,
    QDialogButtonBox,
    QFileDialog,
    QFormLayout,
    QHBoxLayout,
    QLabel,
    QLineEdit,
    QListWidget,
    QListWidgetItem,
    QMessageBox,
    QPushButton,
    QTextEdit,
    QTreeWidget,
    QTreeWidgetItem,
    QVBoxLayout,
    QWidget,
)

from ..i18n import _
from ..prompts.prompt_manager import PromptManager

log = logging.getLogger(__name__)


# ------------------------------------------------------------------ #
# Edit / Create dialog
# ------------------------------------------------------------------ #


class EditPromptDialog(QDialog):
    """Modal dialog for creating or editing a single prompt."""

    def __init__(
        self,
        mgr: PromptManager,
        prompt_id: str | None = None,
        parent: QWidget | None = None,
    ) -> None:
        super().__init__(parent)
        self._mgr = mgr
        self._prompt_id = prompt_id
        self._existing = mgr.get_prompt(prompt_id) if prompt_id else None

        self.setWindowTitle(
            _("prompts.edit_title_edit") if self._existing else _("prompts.edit_title_new")
        )
        self.setMinimumWidth(500)
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowType.WindowContextHelpButtonHint)
        self._setup_ui()

    def _setup_ui(self) -> None:
        layout = QVBoxLayout(self)
        form = QFormLayout()
        form.setRowWrapPolicy(QFormLayout.RowWrapPolicy.WrapLongRows)

        # Name
        self._name_edit = QLineEdit()
        self._name_edit.setPlaceholderText(_("prompts.edit_name_placeholder"))
        if self._existing:
            self._name_edit.setText(self._existing.get("name", ""))
        form.addRow(_("prompts.edit_name_label"), self._name_edit)

        # Category (editable combo — shows existing categories)
        self._category_edit = QComboBox()
        self._category_edit.setEditable(True)
        le = self._category_edit.lineEdit()
        if le is not None:
            le.setPlaceholderText(_("prompts.edit_category_placeholder"))
        existing_cats = sorted(
            {p.get("category", "") for p in self._mgr.list_prompts() if p.get("category", "")}
        )
        for cat in existing_cats:
            self._category_edit.addItem(cat)
        if self._existing:
            self._category_edit.setCurrentText(self._existing.get("category", ""))
        else:
            self._category_edit.setCurrentText("")
        form.addRow(_("prompts.edit_category_label"), self._category_edit)

        # Shortcut
        self._shortcut_edit = QLineEdit()
        self._shortcut_edit.setPlaceholderText(_("prompts.edit_shortcut_placeholder"))
        if self._existing:
            self._shortcut_edit.setText(self._existing.get("shortcut", ""))
        form.addRow(_("prompts.edit_shortcut_label"), self._shortcut_edit)

        # Description
        self._desc_edit = QLineEdit()
        self._desc_edit.setPlaceholderText(_("prompts.edit_desc_placeholder"))
        if self._existing:
            self._desc_edit.setText(self._existing.get("description", ""))
        form.addRow(_("prompts.edit_desc_label"), self._desc_edit)

        # Prompt text
        self._prompt_edit = QTextEdit()
        self._prompt_edit.setPlaceholderText(_("prompts.edit_prompt_placeholder"))
        self._prompt_edit.setMinimumHeight(120)
        if self._existing:
            self._prompt_edit.setPlainText(self._existing.get("prompt", ""))
        form.addRow(_("prompts.edit_prompt_label"), self._prompt_edit)

        # Tags
        self._tags_edit = QLineEdit()
        self._tags_edit.setPlaceholderText(_("prompts.edit_tags_placeholder"))
        if self._existing:
            self._tags_edit.setText(", ".join(self._existing.get("tags", [])))
        form.addRow(_("prompts.edit_tags_label"), self._tags_edit)

        layout.addLayout(form)

        # Shortcut validation hint
        self._shortcut_hint = QLabel("")
        self._shortcut_hint.setStyleSheet("color: #cc0000; font-size: 11px;")
        layout.addWidget(self._shortcut_hint)

        # Buttons
        buttons = QDialogButtonBox(
            QDialogButtonBox.StandardButton.Save | QDialogButtonBox.StandardButton.Cancel
        )
        save_btn = buttons.button(QDialogButtonBox.StandardButton.Save)
        cancel_btn = buttons.button(QDialogButtonBox.StandardButton.Cancel)
        if save_btn is not None:
            save_btn.setText(_("prompts.btn_save"))
        if cancel_btn is not None:
            cancel_btn.setText(_("prompts.btn_cancel"))
        buttons.accepted.connect(self._on_save)
        buttons.rejected.connect(self.reject)
        layout.addWidget(buttons)

    def _on_save(self) -> None:
        name = self._name_edit.text().strip()
        prompt_text = self._prompt_edit.toPlainText().strip()

        if not name:
            QMessageBox.warning(self, _("prompts.edit_title_new"), _("prompts.empty_name"))
            self._name_edit.setFocus()
            return
        if not prompt_text:
            QMessageBox.warning(self, _("prompts.edit_title_new"), _("prompts.empty_prompt"))
            self._prompt_edit.setFocus()
            return

        shortcut = self._shortcut_edit.text().strip()
        if shortcut and not re.match(r"^/[a-z0-9_-]+$", shortcut):
            self._shortcut_hint.setText(_("prompts.edit_shortcut_error"))
            self._shortcut_edit.setFocus()
            return
        self._shortcut_hint.setText("")

        raw_tags = self._tags_edit.text()
        tags = [t.strip() for t in raw_tags.replace(",", " ").split() if t.strip()]

        data: dict = {
            "name": name,
            "prompt": prompt_text,
            "category": self._category_edit.currentText().strip(),
            "shortcut": shortcut,
            "description": self._desc_edit.text().strip(),
            "tags": tags,
        }
        if self._existing:
            data["id"] = self._existing["id"]

        try:
            self._mgr.save_prompt(data)
        except ValueError as exc:
            self._shortcut_hint.setText(_("prompts.edit_shortcut_duplicate", shortcut=shortcut))
            log.warning("Save prompt failed: %s", exc)
            self._shortcut_edit.setFocus()
            return

        self.accept()


# ------------------------------------------------------------------ #
# Main prompts management window
# ------------------------------------------------------------------ #


class PromptsWindow(QDialog):
    """Full-featured prompts management window with QTreeWidget grouped by category."""

    def __init__(
        self,
        mgr: PromptManager,
        on_use_prompt: Callable[[str], None] | None = None,
        parent: QWidget | None = None,
    ) -> None:
        super().__init__(parent)
        self._mgr = mgr
        self._on_use_prompt = on_use_prompt
        self.setWindowTitle(_("prompts.window_title"))
        self.setMinimumSize(720, 500)
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowType.WindowContextHelpButtonHint)
        self._setup_ui()
        self._rebuild_tree()

    def _setup_ui(self) -> None:
        layout = QVBoxLayout(self)

        # Search bar
        search_row = QHBoxLayout()
        self._search_edit = QLineEdit()
        self._search_edit.setPlaceholderText(_("prompts.search_placeholder"))
        self._search_edit.textChanged.connect(self._on_search_changed)
        search_row.addWidget(self._search_edit)
        layout.addLayout(search_row)

        # Tree widget (3 columns: Name, Shortcut, Used)
        self._tree = QTreeWidget()
        self._tree.setColumnCount(3)
        self._tree.setHeaderLabels(
            [
                _("prompts.col_name"),
                _("prompts.col_shortcut"),
                _("prompts.col_count"),
            ]
        )
        self._tree.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
        self._tree.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
        self._tree.setAlternatingRowColors(True)
        hdr = self._tree.header()
        if hdr is not None:
            hdr.setStretchLastSection(False)
            hdr.setSectionResizeMode(0, hdr.ResizeMode.Stretch)
            hdr.setSectionResizeMode(1, hdr.ResizeMode.ResizeToContents)
            hdr.setSectionResizeMode(2, hdr.ResizeMode.ResizeToContents)
        self._tree.itemDoubleClicked.connect(self._on_item_double_clicked)
        layout.addWidget(self._tree)

        # Button row
        btn_row = QHBoxLayout()
        self._btn_create = QPushButton(_("prompts.btn_create"))
        self._btn_create.clicked.connect(self._on_create)
        self._btn_edit = QPushButton(_("prompts.btn_edit"))
        self._btn_edit.clicked.connect(self._on_edit)
        self._btn_delete = QPushButton(_("prompts.btn_delete"))
        self._btn_delete.clicked.connect(self._on_delete)
        self._btn_use = QPushButton(_("prompts.btn_use"))
        self._btn_use.clicked.connect(self._on_use)
        self._btn_import = QPushButton(_("prompts.btn_import"))
        self._btn_import.clicked.connect(self._on_import)
        self._btn_export = QPushButton(_("prompts.btn_export"))
        self._btn_export.clicked.connect(self._on_export)
        self._btn_close = QPushButton(_("prompts.btn_close"))
        self._btn_close.clicked.connect(self.accept)

        btn_row.addWidget(self._btn_create)
        btn_row.addWidget(self._btn_edit)
        btn_row.addWidget(self._btn_delete)
        btn_row.addWidget(self._btn_import)
        btn_row.addWidget(self._btn_export)
        btn_row.addStretch()
        btn_row.addWidget(self._btn_use)
        btn_row.addWidget(self._btn_close)
        layout.addLayout(btn_row)

    # ------------------------------------------------------------------ #
    # Tree building
    # ------------------------------------------------------------------ #

    def _rebuild_tree(self) -> None:
        """Rebuild the QTreeWidget (grouped by category or flat for search)."""
        self._tree.clear()

        query = self._search_edit.text().strip() if hasattr(self, "_search_edit") else ""

        if query:
            # Flat search mode
            prompts = self._mgr.list_prompts(search=query)
            for p in prompts:
                self._tree.addTopLevelItem(self._make_prompt_item(p))
        else:
            # Grouped mode
            by_cat = self._mgr.get_prompts_by_category()
            for cat, prompts in by_cat.items():
                cat_label = cat if cat else _("prompts.category_none")
                cat_item = QTreeWidgetItem([cat_label, "", ""])
                font = QFont()
                font.setBold(True)
                cat_item.setFont(0, font)
                cat_item.setFlags(cat_item.flags() & ~Qt.ItemFlag.ItemIsSelectable)
                for p in prompts:
                    cat_item.addChild(self._make_prompt_item(p))
                self._tree.addTopLevelItem(cat_item)
                cat_item.setExpanded(True)

    @staticmethod
    def _make_prompt_item(p: dict) -> QTreeWidgetItem:
        shortcut = p.get("shortcut", "")
        count = str(p.get("usage_count", 0))
        item = QTreeWidgetItem([p.get("name", ""), shortcut, count])
        item.setData(0, Qt.ItemDataRole.UserRole, p.get("id"))
        return item

    def _selected_prompt_id(self) -> str | None:
        item = self._tree.currentItem()
        if item is None:
            return None
        return item.data(0, Qt.ItemDataRole.UserRole)  # None for category header nodes

    def _on_search_changed(self) -> None:
        QTimer.singleShot(150, self._rebuild_tree)

    def _on_item_double_clicked(self, item: QTreeWidgetItem, _col: int) -> None:
        if item.data(0, Qt.ItemDataRole.UserRole):
            self._on_edit()

    # ------------------------------------------------------------------ #
    # Button handlers
    # ------------------------------------------------------------------ #

    def _on_create(self) -> None:
        dlg = EditPromptDialog(self._mgr, parent=self)
        if dlg.exec() == QDialog.DialogCode.Accepted:
            self._rebuild_tree()

    def _on_edit(self) -> None:
        pid = self._selected_prompt_id()
        if not pid:
            QMessageBox.information(self, _("prompts.window_title"), _("prompts.no_selection"))
            return
        dlg = EditPromptDialog(self._mgr, prompt_id=pid, parent=self)
        if dlg.exec() == QDialog.DialogCode.Accepted:
            self._rebuild_tree()

    def _on_delete(self) -> None:
        pid = self._selected_prompt_id()
        if not pid:
            QMessageBox.information(self, _("prompts.window_title"), _("prompts.no_selection"))
            return
        prompt = self._mgr.get_prompt(pid)
        name = prompt.get("name", pid) if prompt else pid
        answer = QMessageBox.question(
            self,
            _("prompts.delete_title"),
            _("prompts.delete_question", name=name),
        )
        if answer == QMessageBox.StandardButton.Yes:
            self._mgr.delete_prompt(pid)
            self._rebuild_tree()

    def _on_use(self) -> None:
        pid = self._selected_prompt_id()
        if not pid:
            QMessageBox.information(self, _("prompts.window_title"), _("prompts.no_selection"))
            return
        if self._on_use_prompt:
            self._on_use_prompt(pid)
        self.accept()

    def _on_import(self) -> None:
        path, _filter = QFileDialog.getOpenFileName(
            self,
            _("prompts.import_title"),
            "",
            "JSON (*.json)",
        )
        if not path:
            return
        try:
            count = self._mgr.import_prompts(path, merge=True)
            QMessageBox.information(
                self,
                _("prompts.import_title"),
                _("prompts.import_success", count=count),
            )
            self._rebuild_tree()
        except Exception as exc:
            QMessageBox.warning(
                self,
                _("prompts.import_title"),
                _("prompts.import_error", error=str(exc)),
            )

    def _on_export(self) -> None:
        path, _filter = QFileDialog.getSaveFileName(
            self,
            _("prompts.export_title"),
            "prompts.json",
            "JSON (*.json)",
        )
        if not path:
            return
        try:
            count = self._mgr.export_prompts(path)
            QMessageBox.information(
                self,
                _("prompts.export_title"),
                _("prompts.export_success", count=count, path=path),
            )
        except Exception as exc:
            QMessageBox.warning(
                self,
                _("prompts.export_title"),
                _("prompts.export_error", error=str(exc)),
            )


# ------------------------------------------------------------------ #
# Quick-select popup (for hotkey) — with slash mode
# ------------------------------------------------------------------ #


class QuickSelectDialog(QDialog):
    """Compact prompts picker shown on the prompts hotkey.

    The user types to filter and presses Enter (or double-clicks) to use.
    When search text starts with ``/``, slash mode activates — prompts are
    filtered by their shortcut and the trailing text is passed as
    ``text_override`` to ``on_use_prompt``.
    """

    def __init__(
        self,
        mgr: PromptManager,
        on_use_prompt: Callable[..., None] | None = None,
        slash_enabled: bool = True,
        parent: QWidget | None = None,
    ) -> None:
        super().__init__(parent)
        self._mgr = mgr
        self._on_use_prompt = on_use_prompt
        self._slash_enabled = slash_enabled
        self._all_prompts: list[dict] = []
        self._slash_mode = False
        self._extra_text = ""

        self.setWindowTitle(_("prompts.quick_title"))
        self.setMinimumWidth(400)
        self.setWindowFlags(
            Qt.WindowType.Dialog
            | Qt.WindowType.FramelessWindowHint
            | Qt.WindowType.WindowStaysOnTopHint
        )
        self._setup_ui()
        self._load_prompts()

    def _setup_ui(self) -> None:
        layout = QVBoxLayout(self)
        layout.setContentsMargins(6, 6, 6, 6)
        layout.setSpacing(4)

        self._search = QLineEdit()
        self._search.setPlaceholderText(_("prompts.quick_search"))
        self._search.textChanged.connect(self._on_search_changed)
        layout.addWidget(self._search)

        self._list = QListWidget()
        self._list.setAlternatingRowColors(True)
        self._list.doubleClicked.connect(self._use_selected)
        self._list.setMinimumHeight(200)
        layout.addWidget(self._list)

        self._hint = QLabel()
        self._hint.setStyleSheet("color: #888; font-size: 11px;")
        layout.addWidget(self._hint)

    def _load_prompts(self) -> None:
        self._all_prompts = self._mgr.list_prompts()
        self._on_search_changed(self._search.text())
        if not self._all_prompts:
            self._hint.setText(_("prompts.quick_no_prompts"))

    def _on_search_changed(self, text: str) -> None:
        if self._slash_enabled and text.startswith("/"):
            self._slash_mode = True
            self._populate_slash_mode(text)
        else:
            self._slash_mode = False
            self._extra_text = ""
            self._populate_normal_mode(text)

    def _populate_slash_mode(self, text: str) -> None:
        """Filter prompts by shortcut prefix; capture extra text after shortcut."""
        shortcut_part, _sep, extra = text.partition(" ")
        self._extra_text = extra.strip()
        self._list.clear()

        for p in self._all_prompts:
            sc = p.get("shortcut", "")
            if not sc:
                continue
            if sc.lower().startswith(shortcut_part.lower()):
                desc = p.get("description", "")
                label = f"{sc} — {p.get('name', '')}"
                if desc:
                    label += f"  [{desc}]"
                item = QListWidgetItem(label)
                item.setData(Qt.ItemDataRole.UserRole, p.get("id"))
                self._list.addItem(item)

        if self._list.count() > 0:
            self._list.setCurrentRow(0)
        self._hint.setText(_("prompts.slash_mode_hint"))

    def _populate_normal_mode(self, text: str) -> None:
        """Filter prompts by name/prompt text."""
        query = text.strip().lower()
        self._list.clear()
        for p in self._all_prompts:
            if (
                query
                and query not in p.get("name", "").lower()
                and query not in p.get("prompt", "").lower()
            ):
                continue
            tags_str = ", ".join(p.get("tags", []))
            label = p.get("name", "")
            if tags_str:
                label += f"  [{tags_str}]"
            item = QListWidgetItem(label)
            item.setData(Qt.ItemDataRole.UserRole, p.get("id"))
            self._list.addItem(item)

        if self._list.count() > 0:
            self._list.setCurrentRow(0)
        self._hint.setText("")

    def keyPressEvent(self, event) -> None:
        if event.key() in (Qt.Key.Key_Return, Qt.Key.Key_Enter):
            self._use_selected()
        elif event.key() == Qt.Key.Key_Escape:
            self.reject()
        elif event.key() == Qt.Key.Key_Down and self._list.hasFocus():
            super().keyPressEvent(event)
        else:
            super().keyPressEvent(event)

    def _use_selected(self) -> None:
        item = self._list.currentItem()
        if item is None:
            return
        pid = item.data(Qt.ItemDataRole.UserRole)
        if pid and self._on_use_prompt:
            if self._slash_mode and self._extra_text:
                self._on_use_prompt(pid, self._extra_text)
            else:
                self._on_use_prompt(pid)
        self.accept()
