# 中文注释：
# 文件：echobot/config/loader.py
# 说明：配置加载与配置模型定义。

"""Configuration loading utilities."""

import json
from pathlib import Path
from typing import Any

from echobot.config.schema import Config, MultiAgentsConfig


def get_config_path(instance: str | None = None) -> Path:
    """
    Get the configuration file path.

    支持多实例隔离架构。如果不指定 instance，返回默认配置文件路径；
    如果指定 instance，返回该实例的配置文件路径。

    参数:
        instance: 可选的实例名称。如果提供，返回该实例的配置文件路径；
                 如果为 None，返回默认配置文件路径。

    返回:
        Path: 配置文件的路径

    示例:
        >>> get_config_path()
        PosixPath('/home/user/.echobot/config.json')
        >>> get_config_path(instance="admin")
        PosixPath('/home/user/.echobot/instances/admin/config.json')
    """
    if instance:
        # 返回实例特定的配置文件路径
        return Path.home() / ".echobot" / "instances" / instance / "config.json"
    # 返回默认配置文件路径（向后兼容）
    return Path.home() / ".echobot" / "config.json"


def get_data_dir() -> Path:
    """Get the echobot data directory."""
    from echobot.utils.helpers import get_data_path

    return get_data_path()


def load_config(config_path: Path | None = None, instance: str | None = None) -> Config:
    """
    Load configuration from file or create default.

    支持多实例隔离架构。可以通过 instance 参数指定要加载哪个实例的配置。
    如果实例的配置文件不存在，会自动创建默认配置。

    参数:
        config_path: 可选的配置文件路径。如果提供，直接使用该路径；
                    如果为 None，根据 instance 参数确定路径。
        instance: 可选的实例名称。如果提供，加载该实例的配置；
                 如果为 None，加载默认配置。

    返回:
        Config: 加载的配置对象

    示例:
        >>> load_config()
        Config(...)  # 加载默认配置
        >>> load_config(instance="admin")
        Config(...)  # 加载 admin 实例的配置
    """
    path = config_path or get_config_path(instance)

    if path.exists():
        try:
            with open(path) as f:
                data = json.load(f)
            return Config.model_validate(convert_keys(data))
        except (json.JSONDecodeError, ValueError) as e:
            print(f"Warning: Failed to load config from {path}: {e}")
            print("Using default configuration.")
    else:
        # 配置文件不存在，创建默认配置
        if instance:
            # 确保实例目录结构存在
            from echobot.utils.instance import ensure_instance_structure

            ensure_instance_structure(instance)
            # 现在配置应该已经创建了，如果没有则返回默认
            if path.exists():
                try:
                    with open(path) as f:
                        data = json.load(f)
                    return Config.model_validate(convert_keys(data))
                except (json.JSONDecodeError, ValueError) as e:
                    print(f"Warning: Failed to load config from {path}: {e}")

    return Config()


def save_config(
    config: Config, config_path: Path | None = None, instance: str | None = None
) -> None:
    """
    Save configuration to file.

    支持多实例隔离架构。可以通过 instance 参数指定要保存到哪个实例的配置文件。

    参数:
        config: 要保存的配置对象
        config_path: 可选的配置文件路径。如果提供，直接保存到该路径；
                    如果为 None，根据 instance 参数确定路径。
        instance: 可选的实例名称。如果提供，保存到该实例的配置文件；
                 如果为 None，保存到默认配置文件。

    示例:
        >>> save_config(config)
        # 保存到默认配置文件
        >>> save_config(config, instance="admin")
        # 保存到 admin 实例的配置文件
    """
    path = config_path or get_config_path(instance)
    path.parent.mkdir(parents=True, exist_ok=True)

    # Convert to camelCase format
    data = config.model_dump()
    data = convert_to_camel(data)

    # 清理空的 providers
    if "providers" in data and isinstance(data["providers"], dict):
        cleaned_providers = {}
        for name, provider in data["providers"].items():
            # 只保留有实际配置的 provider
            if provider and isinstance(provider, dict):
                # 清理 null 值
                cleaned = {k: v for k, v in provider.items() if v is not None and v != ""}
                if cleaned:
                    cleaned_providers[name] = cleaned
        data["providers"] = cleaned_providers

    with open(path, "w") as f:
        json.dump(data, f, indent=2)


def convert_keys(data: Any) -> Any:
    """Convert camelCase keys to snake_case for Pydantic."""
    if isinstance(data, dict):
        return {camel_to_snake(k): convert_keys(v) for k, v in data.items()}
    if isinstance(data, list):
        return [convert_keys(item) for item in data]
    return data


def convert_to_camel(data: Any) -> Any:
    """Convert snake_case keys to camelCase."""
    if isinstance(data, dict):
        return {snake_to_camel(k): convert_to_camel(v) for k, v in data.items()}
    if isinstance(data, list):
        return [convert_to_camel(item) for item in data]
    return data


def camel_to_snake(name: str) -> str:
    """Convert camelCase to snake_case."""
    result = []
    for i, char in enumerate(name):
        if char.isupper() and i > 0:
            result.append("_")
        result.append(char.lower())
    return "".join(result)


def snake_to_camel(name: str) -> str:
    """Convert snake_case to camelCase."""
    components = name.split("_")
    return components[0] + "".join(x.title() for x in components[1:])


# ============================================================================
# Multi-Agent Configuration Loading
# ============================================================================


def get_agents_config_path() -> Path:
    """Get the multi-agent configuration file path."""
    return Path.home() / ".echobot" / "agents.json"


def load_agents_config(config_path: Path | None = None) -> MultiAgentsConfig:
    """
    Load multi-agent configuration from JSON file.

    Args:
        config_path: Optional path to agents.json. If None, uses default path.

    Returns:
        MultiAgentsConfig: Loaded configuration
    """
    path = config_path or get_agents_config_path()

    if not path.exists():
        # Return empty config if file doesn't exist
        return MultiAgentsConfig()

    try:
        with open(path) as f:
            data = json.load(f)
        # Convert camelCase to snake_case for Pydantic
        data = convert_keys(data)
        return MultiAgentsConfig.model_validate(data)
    except (json.JSONDecodeError, ValueError) as e:
        print(f"Warning: Failed to load agents config from {path}: {e}")
        return MultiAgentsConfig()


def save_agents_config(config: MultiAgentsConfig, config_path: Path | None = None) -> None:
    """
    Save multi-agent configuration to JSON file.

    Args:
        config: Configuration to save
        config_path: Optional path. If None, uses default path.
    """
    path = config_path or get_agents_config_path()
    path.parent.mkdir(parents=True, exist_ok=True)

    # Convert to camelCase for JSON
    data = config.model_dump()
    data = convert_to_camel(data)

    with open(path, "w") as f:
        json.dump(data, f, indent=2)
