"""
配置管理模块

负责加载和管理系统配置，支持从 YAML 文件和环境变量读取配置。
配置加载顺序：默认配置 -> YAML 文件 -> 环境变量
"""

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

import yaml
from dotenv import load_dotenv


class Config:
    """配置管理类"""

    def __init__(self, config_path: Optional[str] = None):
        """
        初始化配置管理器

        Args:
            config_path: 配置文件路径，如果为 None 则使用默认路径
        """
        self._config: Dict[str, Any] = {}
        self._load_env_file()
        self._load_config_file(config_path)
        self._load_env_variables()
        self._validate_config()

    def _load_env_file(self):
        """加载 .env 文件"""
        env_path = Path(__file__).parent.parent.parent / ".env"
        if env_path.exists():
            load_dotenv(env_path)

    def _load_config_file(self, config_path: Optional[str] = None):
        """
        从 YAML 文件加载配置

        Args:
            config_path: 配置文件路径
        """
        path: Path
        if config_path is None:
            # 默认配置文件路径 - 优先查找多个可能的位置
            possible_paths = [
                # 1. 开发环境：项目根目录
                Path(__file__).parent.parent.parent / "config" / "config.yaml",
                # 2. uvx 安装：site-packages/src/config/config.yaml
                Path(__file__).parent / "config" / "config.yaml",
                # 3. 打包安装：与 src 同级的 config
                Path(__file__).parent.parent / "config" / "config.yaml",
            ]
            
            path = None
            for p in possible_paths:
                if p.exists():
                    path = p
                    break
            
            if path is None:
                raise FileNotFoundError(
                    f"配置文件不存在，已尝试以下路径：\n" +
                    "\n".join(f"  - {p}" for p in possible_paths)
                )
        else:
            path = Path(config_path)

        if not path.exists():
            raise FileNotFoundError(f"配置文件不存在: {path}")

        try:
            with open(path, "r", encoding="utf-8") as f:
                self._config = yaml.safe_load(f) or {}
        except yaml.YAMLError as e:
            raise ValueError(f"配置文件格式错误: {e}")
        except Exception as e:
            raise Exception(f"加载配置文件失败: {e}")

    def _load_env_variables(self):
        """从环境变量加载配置，覆盖配置文件中的值"""
        # Embedding API 配置
        if os.getenv("EMBEDDING_API_KEY"):
            self._set_nested("embedding.api_key", os.getenv("EMBEDDING_API_KEY"))
        if os.getenv("EMBEDDING_API_ENDPOINT"):
            self._set_nested(
                "embedding.api_endpoint", os.getenv("EMBEDDING_API_ENDPOINT")
            )
        if os.getenv("EMBEDDING_MODEL"):
            self._set_nested("embedding.model", os.getenv("EMBEDDING_MODEL"))

        # 外部 API 配置
        if os.getenv("EXTERNAL_API_KEY"):
            self._set_nested(
                "import.external_api.api_key", os.getenv("EXTERNAL_API_KEY")
            )
        if os.getenv("EXTERNAL_API_ENDPOINT"):
            self._set_nested(
                "import.external_api.endpoint", os.getenv("EXTERNAL_API_ENDPOINT")
            )

        # 数据库配置
        if os.getenv("SQLITE_DB_PATH"):
            self._set_nested("database.sqlite.db_path", os.getenv("SQLITE_DB_PATH"))
        if os.getenv("CHROMA_PERSIST_DIR"):
            self._set_nested(
                "database.chromadb.persist_dir", os.getenv("CHROMA_PERSIST_DIR")
            )
        if os.getenv("CHROMA_COLLECTION_NAME"):
            self._set_nested(
                "database.chromadb.collection_name", os.getenv("CHROMA_COLLECTION_NAME")
            )

        # 日志配置
        log_level = os.getenv("LOG_LEVEL")
        if log_level:
            self._set_nested("logging.level", log_level.upper())
        if os.getenv("LOG_FILE_PATH"):
            self._set_nested("logging.file.path", os.getenv("LOG_FILE_PATH"))
        if os.getenv("ERROR_LOG_FILE_PATH"):
            self._set_nested(
                "logging.error_file.path", os.getenv("ERROR_LOG_FILE_PATH")
            )

        # 服务器配置
        if os.getenv("SERVER_HOST"):
            self._set_nested("server.host", os.getenv("SERVER_HOST"))
        server_port = os.getenv("SERVER_PORT")
        if server_port:
            self._set_nested("server.port", int(server_port))

        # 性能配置
        batch_size = os.getenv("BATCH_SIZE")
        if batch_size:
            self._set_nested("import.batch_size", int(batch_size))
        max_retries = os.getenv("MAX_RETRIES")
        if max_retries:
            self._set_nested("embedding.max_retries", int(max_retries))
        embedding_cache_size = os.getenv("EMBEDDING_CACHE_SIZE")
        if embedding_cache_size:
            self._set_nested(
                "cache.embedding_cache.max_size", int(embedding_cache_size)
            )
        query_cache_size = os.getenv("QUERY_CACHE_SIZE")
        if query_cache_size:
            self._set_nested("cache.query_cache.max_size", int(query_cache_size))

        # 功能开关
        auto_backup_enabled = os.getenv("AUTO_BACKUP_ENABLED")
        if auto_backup_enabled:
            self._set_nested(
                "data_management.backup.enabled",
                auto_backup_enabled.lower() == "true",
            )
        consistency_check_enabled = os.getenv("CONSISTENCY_CHECK_ENABLED")
        if consistency_check_enabled:
            self._set_nested(
                "data_management.consistency_check.enabled",
                consistency_check_enabled.lower() == "true",
            )
        rate_limit_enabled = os.getenv("RATE_LIMIT_ENABLED")
        if rate_limit_enabled:
            self._set_nested(
                "security.rate_limit.enabled",
                rate_limit_enabled.lower() == "true",
            )
        cache_enabled = os.getenv("CACHE_ENABLED")
        if cache_enabled:
            enabled = cache_enabled.lower() == "true"
            self._set_nested("cache.embedding_cache.enabled", enabled)
            self._set_nested("cache.query_cache.enabled", enabled)
            self._set_nested("cache.statistics_cache.enabled", enabled)

        # 环境配置
        if os.getenv("ENVIRONMENT"):
            self._set_nested("server.environment", os.getenv("ENVIRONMENT"))
        debug_mode = os.getenv("DEBUG")
        if debug_mode:
            self._set_nested("server.debug", debug_mode.lower() == "true")

    def _set_nested(self, key: str, value: Any):
        """
        设置嵌套键值

        Args:
            key: 点分隔的键路径，如 'database.sqlite.db_path'
            value: 要设置的值
        """
        keys = key.split(".")
        d = self._config
        for k in keys[:-1]:
            if k not in d:
                d[k] = {}
            d = d[k]
        d[keys[-1]] = value

    def _get_nested(self, key: str, default: Any = None) -> Any:
        """
        获取嵌套键值

        Args:
            key: 点分隔的键路径
            default: 默认值

        Returns:
            配置值或默认值
        """
        keys = key.split(".")
        d = self._config
        for k in keys:
            if not isinstance(d, dict) or k not in d:
                return default
            d = d[k]
        return d

    def _validate_config(self):
        """验证配置的有效性"""
        errors = []

        # 验证必需的配置项
        required_configs = [
            ("embedding.api_key", "Embedding API 密钥"),
            ("database.sqlite.db_path", "SQLite 数据库路径"),
            ("database.chromadb.persist_dir", "ChromaDB 持久化目录"),
            ("database.chromadb.collection_name", "ChromaDB Collection 名称"),
        ]

        for key, description in required_configs:
            if not self._get_nested(key):
                errors.append(f"缺少必需配置: {description} ({key})")

        # 验证路径配置，自动创建目录
        db_path = self._get_nested("database.sqlite.db_path")
        if db_path:
            db_dir = Path(db_path).parent
            if not db_dir.exists():
                try:
                    db_dir.mkdir(parents=True, exist_ok=True)
                    print(f"[OK] 已创建数据库目录: {db_dir}")
                except Exception as e:
                    print(f"[WARNING] 无法创建数据库目录 {db_dir}: {e}")
                    print(f"[WARNING] 将在首次访问时尝试创建")

        chroma_dir = self._get_nested("database.chromadb.persist_dir")
        if chroma_dir:
            chroma_path = Path(chroma_dir)
            if not chroma_path.exists():
                try:
                    chroma_path.mkdir(parents=True, exist_ok=True)
                    print(f"[OK] 已创建 ChromaDB 目录: {chroma_path}")
                except Exception as e:
                    print(f"[WARNING] 无法创建 ChromaDB 目录 {chroma_path}: {e}")
                    print(f"[WARNING] 将在首次访问时尝试创建")

        # 验证日志目录，不强制要求创建成功
        log_path = self._get_nested("logging.file.path")
        if log_path:
            log_dir = Path(log_path).parent
            if not log_dir.exists():
                try:
                    log_dir.mkdir(parents=True, exist_ok=True)
                    print(f"[OK] 已创建日志目录: {log_dir}")
                except Exception as e:
                    print(f"[WARNING] 无法创建日志目录 {log_dir}: {e}")
                    print(f"[WARNING] 日志将只输出到控制台")

        # 验证数值范围
        batch_size = self._get_nested("import.batch_size", 0)
        if batch_size <= 0 or batch_size > 1000:
            errors.append(f"批处理大小配置不合理: {batch_size} (应在 1-1000 之间)")

        max_retries = self._get_nested("embedding.max_retries", 0)
        if max_retries < 0 or max_retries > 10:
            errors.append(f"最大重试次数配置不合理: {max_retries} (应在 0-10 之间)")

        if errors:
            raise ValueError("配置验证失败:\n" + "\n".join(errors))

    def get(self, key: str, default: Any = None) -> Any:
        """
        获取配置值

        Args:
            key: 配置键，支持点分隔的嵌套键
            default: 默认值

        Returns:
            配置值或默认值
        """
        return self._get_nested(key, default)

    def get_all(self) -> Dict[str, Any]:
        """
        获取所有配置

        Returns:
            完整的配置字典
        """
        return self._config.copy()

    # 便捷访问属性
    @property
    def embedding_api_key(self) -> str:
        """Embedding API 密钥"""
        return self.get("embedding.api_key", "")

    @property
    def embedding_api_endpoint(self) -> str:
        """Embedding API 端点"""
        return self.get("embedding.api_endpoint", "")

    @property
    def embedding_model(self) -> str:
        """Embedding 模型名称"""
        return self.get("embedding.model", "")

    @property
    def embedding_batch_size(self) -> int:
        """Embedding 批量大小"""
        return self.get("embedding.batch_size", 20)

    @property
    def embedding_max_retries(self) -> int:
        """Embedding 最大重试次数"""
        return self.get("embedding.max_retries", 3)

    @property
    def sqlite_db_path(self) -> str:
        """SQLite 数据库路径"""
        return self.get("database.sqlite.db_path", "./data/questions.db")

    @property
    def chromadb_persist_dir(self) -> str:
        """ChromaDB 持久化目录"""
        return self.get("database.chromadb.persist_dir", "./data/chroma")

    @property
    def chromadb_collection_name(self) -> str:
        """ChromaDB Collection 名称"""
        return self.get("database.chromadb.collection_name", "questions_collection")

    @property
    def log_level(self) -> str:
        """日志级别"""
        return self.get("logging.level", "INFO")

    @property
    def log_file_path(self) -> str:
        """日志文件路径"""
        return self.get("logging.file.path", "./logs/app.log")

    @property
    def error_log_file_path(self) -> str:
        """错误日志文件路径"""
        return self.get("logging.error_file.path", "./logs/error.log")

    @property
    def import_batch_size(self) -> int:
        """导入批量大小"""
        return self.get("import.batch_size", 50)

    @property
    def external_api_endpoint(self) -> str:
        """外部 API 端点"""
        return self.get("import.external_api.endpoint", "")

    @property
    def external_api_key(self) -> str:
        """外部 API 密钥"""
        return self.get("import.external_api.api_key", "")

    @property
    def cache_enabled(self) -> bool:
        """是否启用缓存"""
        return self.get("cache.embedding_cache.enabled", True)

    @property
    def embedding_cache_size(self) -> int:
        """向量缓存大小"""
        return self.get("cache.embedding_cache.max_size", 1000)


# 全局配置实例
_config_instance: Optional[Config] = None


def get_config(config_path: Optional[str] = None) -> Config:
    """
    获取配置实例（单例模式）

    Args:
        config_path: 配置文件路径

    Returns:
        配置实例
    """
    global _config_instance
    if _config_instance is None:
        _config_instance = Config(config_path)
    return _config_instance


def reload_config(config_path: Optional[str] = None):
    """
    重新加载配置

    Args:
        config_path: 配置文件路径
    """
    global _config_instance
    _config_instance = Config(config_path)
