"""配置对话框模块."""

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path
from typing import TYPE_CHECKING

from PySide2.QtCore import QModelIndex, Qt, Signal
from PySide2.QtGui import QFont, QStandardItem, QStandardItemModel
from PySide2.QtWidgets import QDialog, QFileDialog, QListView, QMessageBox
from pytola_qtstyles import get_theme_files
from typing_extensions import Literal

from ..models.config import conf
from ..models.engines import ENGINES, check_engine_availability
from ..models.logger import logger
from .configdialog_ui import QRect, QStringListModel, Ui_ConfigDialog

if TYPE_CHECKING:
    from ..gui import FileScanner


ConfigTabName = Literal["styles", "rules", "ignoreDirs", "ignoreFileTypes", "engines"]


@dataclass
class ConfigItem:
    """配置项."""

    name: ConfigTabName
    view: QListView
    model: QStringListModel


class ConfigDialog(QDialog, Ui_ConfigDialog):
    """配置对话框."""

    sig_value_changed = Signal()

    def __init__(self, parent: FileScanner) -> None:
        super().__init__(parent=parent)
        self.setupUi(self)

        self._parent: FileScanner = parent

        # 创建配置项列表
        self.configs: list[ConfigItem] = [
            ConfigItem("styles", self.styles_list, QStringListModel(conf._STYLE_NAMES)),
            ConfigItem("rules", self.rules_list, QStringListModel(conf.KEYWORDS.split(",") if conf.KEYWORDS else [])),
            ConfigItem("ignoreDirs", self.ignore_dirs_list, QStringListModel(conf.IGNORE_DIRS)),
            ConfigItem("ignoreFileTypes", self.ignore_extension_list, QStringListModel(conf.IGNORE_FILE_TYPES)),
        ]

        # 引擎配置使用特殊处理
        self.engine_model = QStandardItemModel()
        self._load_engines()
        self.engines_list.setModel(self.engine_model)
        self.engines_list.clicked.connect(self.on_engine_clicked)

        self._load_settings()
        self._load_styles()
        # 注意:必须在加载样式之后设置字体,因为 QSS 可能会覆盖字体设置
        self._load_fonts()

        self.tabWidget.setCurrentIndex(0)
        self.open_button.setEnabled(False)

        self.styles_list.selectionModel().currentRowChanged.connect(self.on_apply_theme)
        self.add_button.clicked.connect(self.on_add_item)
        self.remove_button.clicked.connect(self.on_remove_item)
        self.open_button.clicked.connect(self.on_open_directory)
        self.tabWidget.currentChanged.connect(self.on_change_tab)
        self.sig_value_changed.connect(self.on_save_settings)

        self._parent.load_theme(conf.DEFAULT_THEME)

    def on_apply_theme(self, current: QModelIndex | None = None, _: QModelIndex | None = None) -> None:
        """应用样式."""
        if not current:
            logger.warning("当前索引无效")
            return

        style_name = current.data() if current.isValid() else None
        if not style_name:
            logger.warning("当前样式无效")
            return

        conf.DEFAULT_THEME = style_name
        self._parent.load_theme(style_name)

        # 重新应用字体,防止被 QSS 覆盖
        self._load_fonts()

    def on_add_item(self) -> None:
        """添加内容."""
        content = self.content_edit.text().strip()
        if not content:
            QMessageBox.warning(self, "警告", "请输入内容!")
            return

        config = self.configs[self.tabWidget.currentIndex()]
        if not config:
            QMessageBox.warning(self, "警告", "请选择要添加的项")
            return

        index = config.view.currentIndex().row()
        current_list = config.model.stringList()
        if content in set(current_list):
            QMessageBox.warning(self, "警告", "内容已存在!")
            self.content_edit.clear()
            return

        if index == -1:
            current_list.append(content)
        else:
            current_list.insert(index, content)
        config.model.setStringList(current_list)

        self.content_edit.clear()
        self.sig_value_changed.emit()

    def on_remove_item(self) -> None:
        """删除内容."""
        config = self.configs[self.tabWidget.currentIndex()]
        if not config:
            QMessageBox.warning(self, "警告", "请选择要删除的项")
            return

        index = config.view.currentIndex().row()
        if index == -1:
            QMessageBox.warning(self, "警告", "请选择要删除的项")
            return

        current_list = config.model.stringList()
        current_list.pop(index)
        config.model.setStringList(current_list)

        self.sig_value_changed.emit()

    def on_open_directory(self) -> None:
        """选择目录."""
        folder = QFileDialog.getExistingDirectory(self, "选择目录")
        if not folder:
            logger.warning("未选择目录")
            return

        self.content_edit.setText(Path(folder).as_posix())

    def on_change_tab(self, index: int) -> None:
        """切换标签页时启用/禁用目录选择按钮."""
        logger.info(f"切换标签页:{index}")

        # 检查是否是引擎标签页(索引 1)
        if index == 1:
            self.open_button.setEnabled(False)
            return

        # 调整索引:因为 configs 列表不包含引擎标签页,所以索引大于 1 时需要减 1
        config_index = index if index < 1 else index - 1

        if config_index < 0 or config_index >= len(self.configs):
            logger.warning(f"配置索引越界:{config_index}, configs 长度:{len(self.configs)}")
            return

        config = self.configs[config_index]
        logger.info(f"当前配置:{config.name}, {config.model.stringList()}")

        if config.name == "ignoreDirs":
            self.open_button.setEnabled(True)
        else:
            self.open_button.setEnabled(False)

    def on_save_settings(self) -> None:
        """保存设置."""
        conf.save_to_json()

    def _load_settings(self) -> None:
        """加载设置."""
        for config in self.configs:
            config.view.setModel(config.model)

        self._parent.setGeometry(
            QRect(conf.WINDOW_POS[0], conf.WINDOW_POS[1], conf.WINDOW_SIZE[0], conf.WINDOW_SIZE[1]),
        )

    def _load_styles(self) -> None:
        """加载样式."""
        theme_files = get_theme_files()
        style_names = list(theme_files.keys())
        conf._STYLE_NAMES = style_names
        self.styles_list.setModel(QStringListModel(style_names))

        current_style = conf.DEFAULT_THEME
        if current_style in style_names:
            index = self.styles_list.model().index(style_names.index(current_style), 0)
            self.styles_list.setCurrentIndex(index)
        else:
            logger.warning(f"当前样式 {current_style} 不存在")

    def _load_fonts(self) -> None:
        """加载字体 - 在应用样式后调用以避免被 QSS 覆盖."""
        font = QFont(conf.DEFAULT_FONT_FAMILY, conf.DEFAULT_FONT_SIZE)

        if conf.FONT_BOLD:
            font.setBold(True)

        if hasattr(self, "_parent") and self._parent is not None:
            self._parent.setFont(font)
        else:
            logger.warning("父窗口不存在,无法设置字体")

    def _load_engines(self) -> None:
        """加载解析引擎列表."""
        self.engine_model.clear()

        for engine in ENGINES:
            # 检查引擎是否可用
            is_available = check_engine_availability(engine)

            # 创建带勾选框的项
            item = QStandardItem(f"{engine.name} - {engine.description}")
            item.setCheckable(True)
            item.setEditable(False)  # 不可编辑文本,但可以勾选

            # 如果引擎可用且已启用,则勾选
            if is_available and engine.name in conf.USE_ENGINES:
                item.setCheckState(Qt.Checked)
            else:
                item.setCheckState(Qt.Unchecked)

            # 如果引擎不可用,禁用该项
            if not is_available:
                item.setEnabled(False)
                item.setToolTip(f"❌ 未安装 {engine.module_name} 模块")
            else:
                item.setToolTip(f"✅ 支持的文件格式:{', '.join(engine.file_extensions)}")

            self.engine_model.appendRow(item)

        # 更新可用引擎列表
        conf.AVAILABLE_ENGINES = [e.name for e in ENGINES if check_engine_availability(e)]

    def on_engine_clicked(self, index: QModelIndex) -> None:
        """引擎勾选状态改变时的处理."""
        item = self.engine_model.itemFromIndex(index)
        if not item or not item.isCheckable():
            return

        engine_name = item.text().split(" - ")[0]  # 提取引擎名称

        # 更新配置
        if item.checkState() == Qt.Checked:
            if engine_name not in conf.USE_ENGINES:
                conf.USE_ENGINES.append(engine_name)
                logger.info(f"已启用引擎:{engine_name}")
        elif engine_name in conf.USE_ENGINES:
            conf.USE_ENGINES.remove(engine_name)
            logger.info(f"已禁用引擎:{engine_name}")

        # 触发保存信号
        self.sig_value_changed.emit()
