"""DBThread module for filescan - 数据库操作线程(单目录异步读取)."""

from __future__ import annotations

import sqlite3
from pathlib import Path
from typing import TYPE_CHECKING

from PySide2.QtCore import QThread, Signal

from ..models.config import conf
from ..models.database import ScanDatabase
from ..models.fileinfo import FileInfo
from ..models.logger import logger

if TYPE_CHECKING:
    from ..gui import FileScanner


class DBThread(QThread):
    """DBThread class for filescan - 数据库操作线程(支持读取和保存)."""

    signal_db_read_finished = Signal(object, Path)  # (文件信息列表,目录路径)
    signal_db_save_finished = Signal(int, int)  # (更新文件数,保存匹配数)
    signal_msg = Signal(str)
    signal_status = Signal(str)

    def __init__(self, parent: FileScanner, directory: Path | None = None) -> None:
        """Initialize the DBThread - 初始化数据库操作线程.

        Args:
            parent: 父组件
            directory: 要读取的数据库所在目录(可选,保存操作时不需要)
        """
        super().__init__(parent)

        self._parent: FileScanner = parent
        self._directory: Path | None = directory
        self._flag_quit = False

        # 保存操作相关属性
        self._results_to_save: list | None = None
        self._db_file: Path | None = None

        # 连接信号到槽函数
        self.signal_db_read_finished.connect(self._parent.on_read_db_finished)
        on_db_save_finished = getattr(self._parent, "on_db_save_finished", None)
        if on_db_save_finished is not None:
            self.signal_db_save_finished.connect(on_db_save_finished)
        self.signal_msg.connect(self._parent.on_update_msg)
        self.signal_status.connect(self._parent.on_update_status)

    def run(self) -> None:
        """Run the thread - 执行线程."""
        # 如果是保存操作
        if self._results_to_save is not None and self._db_file is not None:
            self._save_results_to_db()
            return

        # 否则是读取操作
        if self._directory is None:
            logger.error("[DBThread] 读取操作需要指定目录")
            self.signal_status.emit("<red>❌ 配置错误</red>")
            return

        try:
            self.signal_status.emit(f"<blue>正在加载数据库:[{self._directory.name}]...</blue>")

            result = self.read_db_safe()

            if self._flag_quit:
                self.signal_msg.emit("<red>用户取消操作</red>")
                self.signal_status.emit("<red>已取消</red>")
                return

            if result is not None:
                files_info, db_path = result
                self.signal_db_read_finished.emit(files_info, self._directory)
                self.signal_msg.emit(f"<green>✓ 成功加载数据库:{db_path}</green>")
                self.signal_status.emit("<green>就绪</green>")
            else:
                self.signal_db_read_finished.emit([], self._directory)
                self.signal_msg.emit(f"<red>⚠ 未找到数据库:{self._directory.name}</red>")
                self.signal_status.emit("<red>需要重新扫描</red>")
        except (OSError, RuntimeError) as e:
            logger.error(f"[DBThread] 运行失败:{e}", exc_info=True)
            self.signal_msg.emit(f"<red>✗ 数据库加载失败:{e}</red>")
            self.signal_status.emit("<red>❌ 加载失败</red>")
            self.signal_db_read_finished.emit([], self._directory)

    def save_results_to_db(self, results: list, db_file: Path) -> None:
        """保存结果到数据库.

        Args:
            results: 分析结果列表
            db_file: 数据库文件路径
        """
        self._results_to_save = results
        self._db_file = db_file
        self.start()

    def _save_results_to_db(self) -> None:
        """在后台线程中保存结果到数据库."""
        if self._results_to_save is None or self._db_file is None:
            return

        try:
            db = ScanDatabase(self._db_file)

            total_matches_saved = 0
            files_updated = 0

            # 批量更新文件信息和匹配结果
            for result in self._results_to_save:
                if self._flag_quit:
                    break

                # 确保文件记录存在于数据库中(插入或更新)
                file_id = db.ensure_file_exists_or_insert(
                    root_path=result.fileinfo.root_path,
                    file_path=result.fileinfo.file_path,
                    file_type=result.fileinfo.file_type,
                    file_size=result.fileinfo.file_size,
                    creation_time=result.fileinfo.creation_time,
                    modification_time=result.fileinfo.modification_time,
                    is_skipped=result.fileinfo.is_skipped,
                    skip_reason=result.fileinfo.skip_reason,
                    scan_status=result.fileinfo.scan_status,
                    metadata=result.fileinfo.metadata,
                    file_level=result.fileinfo.file_level,
                    filename_multi_level=result.fileinfo.filename_multi_level,
                    file_content_level=result.fileinfo.file_content_level,
                    matches=result.fileinfo.matches,
                    processing_time_seconds=result.processing_time_seconds,
                    match_count=len(result.matches),
                    analyse_status=result.status,
                )

                # 如果获取到了有效的 file_id,更新它
                if file_id and file_id > 0:
                    result.fileinfo.id = file_id
                    files_updated += 1
                else:
                    logger.warning(f"无法获取文件 ID: {result.fileinfo.file_path}")
                    continue

                # 保存匹配结果
                if result.matches:
                    # 为每个匹配设置正确的 file_id
                    for match in result.matches:
                        match.file_id = file_id

                    saved_count = db.save_file_matches(result.matches)
                    total_matches_saved += saved_count

            db.close()

            logger.info(f"数据库保存完成:更新了 {files_updated} 个文件,保存了 {total_matches_saved} 条匹配记录")

            # 发射保存完成信号
            self.signal_db_save_finished.emit(files_updated, total_matches_saved)

        except (sqlite3.Error, OSError, ValueError, TypeError) as e:
            logger.error(f"保存结果到数据库失败:{e}", exc_info=True)
            self.signal_msg.emit(f"<error>✗ 保存失败:{e}</error>")
            self.signal_status.emit("<red>❌ 保存失败</red>")
        finally:
            # 清理
            self._results_to_save = None
            self._db_file = None

    def quit_read_db(self) -> None:
        """请求退出读取数据库 - 用于中途取消操作."""
        self._flag_quit = True

    def read_db_safe(self) -> tuple[list[FileInfo], str] | None:
        """安全地读取数据库文件,带错误处理和返回值.

        Returns
        -------
            成功时返回 (文件信息列表,数据库路径),失败时返回 None
        """
        if self._directory is None:
            logger.error("[DBThread] 读取操作未指定目录")
            return None

        db_file = self._directory / conf.DB_FILENAME
        if not db_file.exists():
            logger.info(f"路径下没有数据库文件:{self._directory}")
            return None

        try:
            db = ScanDatabase(db_file)
            # 使用 get_all_directory_files 读取所有文件(包含分析结果)
            files_info = db.get_all_directory_files()
            db.close()

            if files_info:
                # 检查是否有分析结果
                analysed_count = sum(1 for f in files_info if getattr(f, "analyse_status", "pending") == "completed")
                total_matches = sum(getattr(f, "match_count", 0) for f in files_info)

                if analysed_count > 0:
                    logger.info(
                        f"成功读取数据库:{self._directory}, 文件数:{len(files_info)}, "
                        f"已分析:{analysed_count}, 匹配数:{total_matches}",
                    )
                else:
                    logger.info(f"成功读取数据库:{self._directory}, 文件数:{len(files_info)} (无分析结果)")
                return files_info, db_file.as_posix()
        except (sqlite3.Error, OSError, ValueError) as e:
            logger.error(f"无法读取数据库文件:{db_file}, 错误:{e}")
            logger.exception(e)
            return None
        else:
            logger.warning(f"数据库为空:{self._directory}")
            return None
