"""ScanWorker module for filescan - 后台扫描工作线程."""

from __future__ import annotations

import concurrent.futures
import time
from typing import TYPE_CHECKING

from PySide2.QtCore import QThread, Signal

if TYPE_CHECKING:
    from ..gui import FileScanner

from ..models.config import conf
from ..models.fileinfo import AnalyseResult, FileInfo
from ..models.logger import logger
from ..rules.analyser import Analyser

# 常量定义
BATCH_SIZE_MIN_UPDATES = 50  # 进度条最小更新次数
EARLY_STAGE_FILE_THRESHOLD = 50  # 早期阶段文件数量阈值
LOG_DETAIL_THRESHOLD = 10  # 详细日志记录的文件数量阈值


class AnalyseThread(QThread):
    """文档扫描后台工作线程."""

    signal_progress_message = Signal(str)
    signal_progress_update = Signal(int, int)
    signal_batch_processed = Signal(int, int, float)  # (processed_count, total_files, elapsed_time)
    signal_analyse_finished = Signal(object)  # (results_list)
    signal_analyse_error = Signal(str)

    def __init__(self, parent: FileScanner, fileinfo_list: list[FileInfo]) -> None:
        """初始化工作线程.

        Args:
            parent: 父窗口对象
            fileinfo_list: 文件信息列表
        """
        super().__init__(parent)

        self.parent: FileScanner = parent
        self.fileinfo_list: list[FileInfo] = fileinfo_list

        # 创建扫描器 - 直接使用规则配置字典
        self.analyser = Analyser()

        # 结果收集
        self.results: list[AnalyseResult] = []

        # 停止标志
        self._flag_quit = False

        # 连接信号到槽函数
        self.signal_progress_update.connect(self.parent._update_progress)
        self.signal_batch_processed.connect(self.parent.on_batch_processed)
        self.signal_analyse_finished.connect(self.parent.on_analyse_finished)
        self.signal_analyse_error.connect(self.parent._scan_error)

    def run(self) -> None:
        """运行文档扫描 - 包含文件名验证."""
        logger.info("[AnalyseThread] run() 方法开始执行")

        # 记录开始时间
        start_time = time.time()

        total_files = len(self.fileinfo_list)
        processed_count = 0
        # 优化:增大批量大小,减少信号发射频率
        # 确保至少有 20 次进度更新,避免进度条长时间不更新
        batch_size = max(20, total_files // BATCH_SIZE_MIN_UPDATES)  # 动态调整,至少 50 次更新

        try:
            with concurrent.futures.ThreadPoolExecutor(max_workers=conf.MAX_WORKERS) as executor:
                # 提交所有任务
                future_to_fileinfo = {
                    executor.submit(self.analyser.analyse, fileinfo): fileinfo for fileinfo in self.fileinfo_list
                }

                # 处理完成的任务
                for future in concurrent.futures.as_completed(future_to_fileinfo):
                    if not self._should_continue_processing():
                        break

                    fileinfo = future_to_fileinfo[future]
                    processed_count += 1
                    result = self._process_future_result(
                        future,
                        fileinfo,
                        processed_count,
                        total_files,
                        batch_size,
                        start_time,
                    )

                    if result is not None:
                        self.results.append(result)

            # 扫描完成,发射完成信号
            if not self._flag_quit:
                logger.info(f"[AnalyseThread] 扫描完成,共处理 {len(self.results)} 个文件")
                self.signal_analyse_finished.emit(self.results)
            else:
                logger.info("[AnalyseThread] 扫描被用户中断")

        except (OSError, RuntimeError, ValueError) as e:
            logger.error(f"[AnalyseThread] 扫描过程中发生严重错误:{e}", exc_info=True)
            self.signal_analyse_error.emit(str(e))

    def _should_continue_processing(self) -> bool:
        """检查是否应该继续处理.

        Returns
        -------
            bool: 如果应该继续返回 True,否则返回 False
        """
        if self._flag_quit:
            logger.info("[AnalyseThread] 收到停止信号,终止扫描")
            return False
        return True

    def _process_future_result(
        self,
        future: concurrent.futures.Future,
        fileinfo: FileInfo,
        processed_count: int,
        total_files: int,
        batch_size: int,
        start_time: float,
    ) -> AnalyseResult | None:
        """处理单个任务的结果.

        Args:
            future: 未来对象
            fileinfo: 文件信息
            processed_count: 已处理文件数
            total_files: 总文件数
            batch_size: 批次大小

        Returns
        -------
            AnalyseResult: 分析结果,如果失败则返回 None
        """
        try:
            result: AnalyseResult = future.result()
        except concurrent.futures.CancelledError:
            logger.warning(f"[AnalyseThread] 任务被取消:{fileinfo.file_path}")
            return None
        except concurrent.futures.TimeoutError:
            logger.error(f"[AnalyseThread] 任务超时:{fileinfo.file_path}")
            return None
        except (OSError, RuntimeError, ValueError) as e:
            logger.error(f"[AnalyseThread] 处理文件失败 {fileinfo.file_path}: {e}", exc_info=True)
            return None
        else:
            # 优化:批量发射信号,减少 UI 刷新频率
            if processed_count % batch_size == 0 or processed_count == total_files:
                self._emit_progress_signals(processed_count, total_files, start_time)

            # 优化:减少日志输出频率
            if processed_count <= LOG_DETAIL_THRESHOLD or processed_count % 1000 == 0 or processed_count == total_files:
                self._log_file_processing(result, processed_count, total_files)
            return result

    def _emit_progress_signals(self, processed_count: int, total_files: int, start_time: float) -> None:
        """发射进度信号和批次处理信号.

        Args:
            processed_count: 已处理文件数
            total_files: 总文件数
            start_time: 开始时间戳
        """
        # 计算已用时间
        elapsed_time = time.time() - start_time

        # 更新进度条
        self.signal_progress_update.emit(processed_count, total_files)

        # 发射批次处理信号,用于在消息列表中显示汇总信息(包含用时)
        self.signal_batch_processed.emit(processed_count, total_files, elapsed_time)

    def _log_file_processing(self, result: AnalyseResult, processed_count: int, total_files: int) -> None:
        """记录文件处理日志.

        Args:
            result: 分析结果
            processed_count: 已处理文件数
            total_files: 总文件数
        """
        logger.debug(
            f"[AnalyseThread] 文件 {processed_count}/{total_files}: "
            f"{result.fileinfo.file_path} - "
            f"{result.fileinfo.match_count} 个匹配,耗时 {result.processing_time_seconds:.4f}s",
        )

    def stop(self) -> None:
        """请求停止扫描."""
        self._flag_quit = True
        logger.info("[AnalyseThread] 收到停止请求")
