"""
AutocoderRulesManager

规则管理器，负责加载、监控和管理规则文件。
实现单例模式，确保全局只有一个规则管理实例。
"""

import os
import re
import time
from threading import Lock
from typing import TYPE_CHECKING

import yaml
from loguru import logger

# 导入数据模型
from ..models.rule_file import RuleFile

# 导入工具函数

# 条件导入以避免循环依赖
if TYPE_CHECKING:
    pass


class AutocoderRulesManager:
    """
    管理 autocoderrules 目录中的规则文件。

    实现单例模式，确保全局只有一个规则管理实例。
    每次访问时都会重新加载规则文件。
    """

    _instance = None
    _lock = Lock()

    def __new__(cls, project_root: str | None = None):
        if not cls._instance:
            with cls._lock:
                if not cls._instance:
                    cls._instance = super().__new__(cls)
                    cls._instance._initialized = False
        return cls._instance

    def __init__(self, project_root: str | None = None):
        if self._initialized:
            return
        self._initialized = True

        self._rules: dict[str, str] = {}  # 存储规则文件内容: {file_path: content}
        self._rules_sources: dict[str, str] = (
            {}
        )  # 存储每个规则文件的来源: {file_path: source}
        self._rules_dir: str | None = None  # 当前使用的规则目录
        self._project_root = (
            project_root if project_root is not None else os.getcwd()
        )  # 项目根目录

        # 加载规则
        self._load_rules()

    def _load_rules(self):
        """
        重新实现的规则加载逻辑，不使用 FinderConfig。
        加载顺序：
        1. 项目级规则文件（按优先级）
        2. 全局规则文件 (~/.auto-coder/autocoderrules)
        3. 全局 repos 子目录规则（基于当前目录名）

        确保不重复加载相同的文件。
        """
        start_time = time.perf_counter()
        logger.info(
            f"[RULES] 开始加载规则 | stage=load project_root={self._project_root}"
        )

        self._rules = {}
        self._rules_sources = {}  # 重置来源信息
        loaded_files: set[str] = set()  # 跟踪已加载的文件，避免重复

        # 统计各来源加载数量
        project_count = len(self._rules)

        # 1. 加载项目级规则文件（按优先级）
        self._load_project_rules(loaded_files)
        project_count = len(self._rules) - project_count

        global_count = len(self._rules)
        # 2. 加载全局规则文件
        self._load_global_rules(loaded_files)
        global_count = len(self._rules) - global_count

        repos_count = len(self._rules)
        # 3. 加载全局 repos 子目录规则
        self._load_global_repos_rules(loaded_files)
        repos_count = len(self._rules) - repos_count

        duration_ms = int((time.perf_counter() - start_time) * 1000)
        logger.info(
            f"[RULES] 规则加载完成 | stage=load total_count={len(self._rules)} project_count={project_count} global_count={global_count} repos_count={repos_count} duration_ms={duration_ms}"
        )

    def _load_project_rules(self, loaded_files: set[str]):
        """加载项目级规则文件，按优先级顺序"""
        project_root = self._project_root

        # 按优先级定义项目级规则目录
        project_dirs = [
            os.path.join(project_root, ".autocoderrules"),
            os.path.join(project_root, ".auto-coder", ".autocoderrules"),
            os.path.join(project_root, ".auto-coder", "autocoderrules"),
        ]

        # 查找第一个包含 .md 文件的目录
        for rules_dir in project_dirs:
            if self._has_md_files(rules_dir):
                self._rules_dir = rules_dir
                logger.info(
                    f"[RULES] 找到项目规则目录 | stage=discover_dirs source=project rules_dir={rules_dir}"
                )
                self._load_rules_from_directory(
                    rules_dir, loaded_files, source="project"
                )
                return

        logger.info(
            f"[RULES] 未找到项目级规则目录，将继续查找全局规则 | stage=discover_dirs project_root={self._project_root}"
        )

    def _load_global_rules(self, loaded_files: set[str]):
        """加载全局规则文件 (~/.auto-coder/autocoderrules)"""
        home_dir = os.path.expanduser("~")
        global_rules_dir = os.path.join(home_dir, ".auto-coder", "autocoderrules")

        if self._has_md_files(global_rules_dir):
            logger.info(
                f"[RULES] 找到全局规则目录 | stage=discover_dirs source=global rules_dir={global_rules_dir}"
            )
            self._load_rules_from_directory(
                global_rules_dir, loaded_files, source="global"
            )
        else:
            logger.info(
                f"[RULES] 未找到全局规则目录，将继续查找 repos 规则 | stage=discover_dirs rules_dir={global_rules_dir}"
            )

    def _load_global_repos_rules(self, loaded_files: set[str]):
        """加载全局 repos 子目录规则（基于当前目录名）"""
        home_dir = os.path.expanduser("~")
        global_rules_dir = os.path.join(home_dir, ".auto-coder", "autocoderrules")
        repos_dir = os.path.join(global_rules_dir, "repos")

        if not os.path.isdir(repos_dir):
            logger.debug(
                f"[RULES] 未找到全局 repos 目录 | stage=discover_dirs repos_dir={repos_dir}"
            )
            return

        # 获取当前目录名
        current_dir_name = os.path.basename(self._project_root)
        repo_specific_dir = os.path.join(repos_dir, current_dir_name)

        if self._has_md_files(repo_specific_dir):
            logger.info(
                f"[RULES] 找到仓库特定规则目录 | stage=discover_dirs source=repos rules_dir={repo_specific_dir}"
            )
            self._load_rules_from_directory(
                repo_specific_dir, loaded_files, source="repos"
            )
        else:
            logger.debug(
                f"[RULES] 未找到仓库特定规则目录 | stage=discover_dirs rules_dir={repo_specific_dir}"
            )

    def _has_md_files(self, directory: str) -> bool:
        """检查目录是否存在且包含 .md 文件"""
        if not os.path.isdir(directory):
            return False

        try:
            for fname in os.listdir(directory):
                if fname.endswith(".md"):
                    return True
            return False
        except Exception as e:
            logger.warning(
                f"[RULES] 检查目录时出错 | stage=discover_dirs directory={directory} exc_type={type(e).__name__} error={e}"
            )
            return False

    def _load_rules_from_directory(
        self, rules_dir: str, loaded_files: set[str], source: str = "unknown"
    ):
        """从指定目录加载规则文件，避免重复加载"""
        start_time = time.perf_counter()
        logger.info(
            f"[RULES] 扫描规则目录开始 | stage=scan rules_dir={rules_dir} source={source}"
        )

        loaded_count = 0
        skipped_count = 0
        file_count = 0

        try:
            for fname in os.listdir(rules_dir):
                if fname.endswith(".md"):
                    file_count += 1
                    fpath = os.path.join(rules_dir, fname)

                    # 使用规范化路径避免重复加载
                    normalized_path = os.path.normpath(os.path.abspath(fpath))
                    if normalized_path in loaded_files:
                        skipped_count += 1
                        logger.debug(
                            f"[RULES] 跳过重复文件 | stage=scan file_path={fpath}"
                        )
                        continue

                    try:
                        with open(fpath, encoding="utf-8") as f:
                            content = f.read()
                            self._rules[fpath] = content
                            self._rules_sources[fpath] = source  # 保存文件来源
                            loaded_files.add(normalized_path)
                            loaded_count += 1
                            logger.debug(
                                f"[RULES] 已加载规则文件 | stage=scan file_path={fpath} source={source} bytes={len(content)}"
                            )
                    except Exception as e:
                        logger.warning(
                            f"[RULES] 加载规则文件时出错 | stage=scan file_path={fpath} exc_type={type(e).__name__} error={e}"
                        )
                        continue

            duration_ms = int((time.perf_counter() - start_time) * 1000)
            logger.info(
                f"[RULES] 扫描规则目录完成 | stage=scan rules_dir={rules_dir} file_count={file_count} loaded_count={loaded_count} skipped_count={skipped_count} duration_ms={duration_ms}"
            )
        except Exception as e:
            logger.warning(
                f"[RULES] 读取规则目录时出错 | stage=scan rules_dir={rules_dir} exc_type={type(e).__name__} error={e}"
            )

    def parse_rule_file(self, file_path: str) -> RuleFile:
        """
        解析规则文件并返回结构化的Pydantic模型对象

        Args:
            file_path: 规则文件的路径

        Returns:
            RuleFile: 包含规则文件结构化内容的Pydantic模型
        """
        if not os.path.exists(file_path) or not file_path.endswith(".md"):
            logger.warning(
                f"[RULES] 无效的规则文件路径 | stage=parse file_path={file_path}"
            )
            return RuleFile(file_path=file_path)

        try:
            with open(file_path, encoding="utf-8") as f:
                content = f.read()

            # 解析YAML头部和Markdown内容
            yaml_pattern = re.compile(r"^---\s*\n(.*?)\n---\s*\n", re.DOTALL)
            yaml_match = yaml_pattern.search(content)

            metadata = {}
            markdown_content = content

            if yaml_match:
                yaml_content = yaml_match.group(1)
                try:
                    metadata = yaml.safe_load(yaml_content)
                    # 移除YAML部分，仅保留Markdown内容
                    markdown_content = content[yaml_match.end() :]
                except Exception as e:
                    logger.warning(
                        f"[RULES] 解析规则文件YAML头部时出错，将忽略YAML头继续解析正文 | stage=parse file_path={file_path} error={e}"
                    )

            # 创建并返回Pydantic模型
            rule = RuleFile(
                description=metadata.get("description", ""),
                globs=metadata.get("globs", []),
                always_apply=metadata.get("alwaysApply", False),
                content=markdown_content.strip(),
                file_path=file_path,
            )
            return rule

        except Exception as e:
            logger.warning(
                f"[RULES] 解析规则文件时出错 | stage=parse file_path={file_path} exc_type={type(e).__name__} error={e}"
            )
            return RuleFile(file_path=file_path)

    def _log_rules_summary(self):
        """打印规则文件列表和来源信息的详细日志"""
        if not self._rules:
            logger.info("[RULES] 规则文件列表为空 | stage=summary")
            return

        # 按来源分组统计
        source_groups: dict[str, list[str]] = {}
        for file_path, source in self._rules_sources.items():
            if source not in source_groups:
                source_groups[source] = []
            source_groups[source].append(file_path)

        # 打印详细的文件列表
        logger.info(
            f"[RULES] 规则文件详细列表 | stage=summary total_count={len(self._rules)}"
        )

        # 按来源打印每个文件
        source_display_names = {
            "project": "当前项目 (project)",
            "global": "全局规则 (global)",
            "repos": "全局仓库规则 (global repos)",
        }

        for source, files in source_groups.items():
            display_name = source_display_names.get(source, source)
            logger.info(f"[RULES]   来源: {display_name} | file_count={len(files)}")
            for file_path in sorted(files):
                file_name = os.path.basename(file_path)
                logger.info(
                    f"[RULES]     - {file_name} | source={source} path={file_path}"
                )

    def get_rules(self) -> dict[str, str]:
        """获取所有规则文件内容，总是重新加载"""
        self._load_rules()  # 总是重新加载
        self._log_rules_summary()  # 打印详细的规则文件列表
        return self._rules.copy()

    def get_parsed_rules(self) -> list[RuleFile]:
        """获取所有解析后的规则文件，总是重新加载"""
        self._load_rules()  # 总是重新加载
        self._log_rules_summary()  # 打印详细的规则文件列表
        parsed_rules = []
        for file_path in self._rules:
            parsed_rule = self.parse_rule_file(file_path)
            parsed_rules.append(parsed_rule)
        return parsed_rules

    @classmethod
    def reset_instance(cls):
        """重置单例实例"""
        with cls._lock:
            if cls._instance is not None:
                cls._instance = None
                logger.info("[RULES] AutocoderRulesManager单例已被重置 | stage=reset")
