import os
import yaml
from typing import Dict, Any, Optional, List
from pathlib import Path


class DocLintConfig:
    """文档检查配置管理器"""

    DEFAULT_CONFIG_FILE = ".stylecheckrrc.yaml"
    DEFAULT_RULES = {
        "paired-punctuation": True,
        "llm-chinese-typo": False
    }
    DEFAULT_LLM_CONFIG = {
        "model": "gpt-3.5-turbo",
        "base_url": "https://api.openai.com/v1",
        "timeout": 30,
        "max_tokens": 1024,
        "cache": True
    }

    def __init__(self, config_path: Optional[str] = None):
        """
        初始化配置管理器
        :param config_path: 配置文件路径，默认使用 .stylecheckrrc.yaml
        """
        self.config_path = config_path or self.DEFAULT_CONFIG_FILE

        # ✅ 先加载原始配置字典（不依赖 self.config）
        raw_config = self._load_raw_config(self.config_path)

        # ✅ 处理继承（使用局部变量，不依赖 self.config）
        self.config = self._resolve_extends(raw_config)

        # ✅ 解析各部分配置
        self.rules_config = self._parse_rules_config()
        self.llm_config = self._parse_llm_config()
        self.ignore_patterns = self._parse_ignore_patterns()
        self.ignore_spec = self._compile_ignore_patterns()

    def _load_raw_config(self, config_path: str) -> Dict[str, Any]:
        """
        加载单个配置文件（不处理继承）
        :param config_path: 配置文件路径
        :return: 配置字典
        """
        if os.path.exists(config_path):
            with open(config_path, 'r', encoding='utf-8') as f:
                return yaml.safe_load(f) or {}
        return {}

    def _resolve_extends(self, config: Dict[str, Any]) -> Dict[str, Any]:
        """
        处理配置继承
        :param config: 当前配置字典
        :return: 合并后的配置字典
        """
        # 1. 获取 extends 字段（使用局部变量，不访问 self.config）
        extends = config.get('extends', '')

        if not extends:
            return config

        # 2. 加载基础配置
        if extends.startswith('package:'):
            # 从包加载
            package_path = extends.replace('package:', '')
            base_config = self._load_package_config(package_path)
        else:
            # 从文件加载
            base_config = self._load_raw_config(extends)

        # 3. 合并配置（当前配置覆盖基础配置）
        return self._merge_config(base_config, config)

    def _load_package_config(self, package_ref: str) -> Dict[str, Any]:
        """
        从 Python 包加载配置
        :param package_ref: 包引用，如 "doclint_config.base"
        :return: 配置字典
        """
        try:
            import importlib.resources as resources
            # 解析包路径和文件名
            parts = package_ref.rsplit('.', 1)
            if len(parts) == 2:
                package, filename = parts
            else:
                package, filename = package_ref, ".stylecheckrrc.yaml"

            # 读取包内资源
            content = resources.files(package).joinpath(filename).read_text(encoding='utf-8')
            return yaml.safe_load(content) or {}
        except Exception as e:
            print(f"[WARN] 加载包配置失败 {package_ref}: {e}")
            return {}

    def _load_file_config(self, file_path: str) -> Dict[str, Any]:
        """从文件加载配置（兼容旧代码）"""
        return self._load_raw_config(file_path)

    def _merge_config(self, base: Dict[str, Any], override: Dict[str, Any]) -> Dict[str, Any]:
        """
        深度合并两个配置字典
        :param base: 基础配置
        :param override: 覆盖配置
        :return: 合并后的配置
        """
        import copy
        result = copy.deepcopy(base)

        for key, value in override.items():
            if key == 'extends':
                # extends 不合并，只用于继承链
                continue
            if key in result and isinstance(result[key], dict) and isinstance(value, dict):
                result[key] = self._merge_config(result[key], value)
            else:
                result[key] = copy.deepcopy(value)

        return result

    def _parse_rules_config(self) -> Dict[str, Dict[str, Any]]:
        """解析规则配置，统一为字典格式"""
        rules_raw = self.config.get('rules', {})
        rules_config = {}

        for rule_id, rule_value in rules_raw.items():
            if isinstance(rule_value, bool):
                rules_config[rule_id] = {"enable": rule_value}
            elif isinstance(rule_value, dict):
                rules_config[rule_id] = {
                    "enable": rule_value.get("enable", True),
                    "severity": rule_value.get("severity"),
                    "model": rule_value.get("model"),
                    **{k: v for k, v in rule_value.items() if k not in ['enable', 'severity', 'model']}
                }
            else:
                rules_config[rule_id] = {"enable": True}

        return rules_config

    def _parse_llm_config(self) -> Dict[str, Any]:
        """解析全局 LLM 配置"""
        llm_raw = self.config.get('llm', {})
        merged = {**self.DEFAULT_LLM_CONFIG, **llm_raw}

        # 处理 API Key（从环境变量读取）
        api_key_env = llm_raw.get('api_key_env', 'DOC_LINT_LLM_KEY')
        merged['api_key'] = os.getenv(api_key_env, '')

        return merged

    def _parse_ignore_patterns(self) -> List[str]:
        """解析忽略文件模式"""
        return self.config.get('ignore', [])

    def is_rule_enabled(self, rule_id: str) -> bool:
        """检查规则是否启用"""
        rule_config = self.rules_config.get(rule_id, {})
        return rule_config.get("enable", False)

    def get_rule_config(self, rule_id: str) -> Dict[str, Any]:
        """获取规则配置（合并全局 LLM 配置）"""
        rule_config = self.rules_config.get(rule_id, {}).copy()
        if rule_id.startswith("llm-"):
            return {**self.llm_config, **rule_config}
        return rule_config

    def get_all_enabled_rules(self) -> List[str]:
        """获取所有启用的规则 ID 列表"""
        return [rule_id for rule_id, config in self.rules_config.items()
                if config.get("enable", False)]

    def _compile_ignore_patterns(self):
        """预编译忽略模式为 pathspec 对象"""
        try:
            import pathspec
            return pathspec.PathSpec.from_lines(
                pathspec.patterns.GitWildMatchPattern,
                self.ignore_patterns
            )
        except ImportError:
            # 降级方案：返回 None，使用手动匹配
            return None

    def should_ignore_file(self, file_path: str) -> bool:
        """检查文件是否应该被忽略"""
        # 方案 A: 使用 pathspec（推荐）
        if self.ignore_spec is not None:
            # pathspec 要求路径使用 / 分隔符
            normalized_path = file_path.replace('\\', '/')
            return self.ignore_spec.match_file(normalized_path)

        # 方案 B: 降级到手动匹配（无 pathspec 时）
        return self._manual_match_ignore(file_path)

    def _manual_match_ignore(self, file_path: str) -> bool:
        """手动实现忽略匹配（降级方案）"""
        import fnmatch
        from pathlib import PurePosixPath

        # 统一使用 / 作为路径分隔符
        normalized = file_path.replace('\\', '/')

        for pattern in self.ignore_patterns:
            # 处理 **/ 开头：匹配任意层级
            if pattern.startswith('**/'):
                core_pattern = pattern[3:]  # 去掉 **/
                # 检查路径的任何部分是否匹配
                parts = normalized.split('/')
                for i in range(len(parts)):
                    sub_path = '/'.join(parts[i:])
                    if fnmatch.fnmatch(sub_path, core_pattern):
                        return True
                continue

            # 处理 /** 结尾：匹配目录下的所有文件
            if pattern.endswith('/**'):
                dir_pattern = pattern[:-3]  # 去掉 /**
                if normalized.startswith(dir_pattern + '/') or normalized == dir_pattern:
                    return True
                continue

            # 处理中间包含 ** 的模式
            if '**' in pattern:
                # 简单处理：检查核心部分是否在路径中
                core = pattern.replace('**/', '').replace('/**', '')
                if core in normalized:
                    return True
                continue

            # 普通 fnmatch 匹配
            if fnmatch.fnmatch(normalized, pattern):
                return True

        return False

    def __repr__(self):
        return f"DocLintConfig(rules={self.get_all_enabled_rules()})"