from __future__ import annotations

import os
from pathlib import Path
from typing import Any

import yaml

_PKG_ROOT = Path(__file__).resolve().parent.parent.parent
_DEFAULT_CONFIG = _PKG_ROOT / "config" / "default.yaml"


def resolve_config_path() -> Path:
    env = os.environ.get("NIANNIAN_CONFIG")
    if env:
        return Path(env).expanduser().resolve()
    return _DEFAULT_CONFIG


def _load_yaml(path: Path) -> dict[str, Any]:
    if not path.is_file():
        return {}
    with path.open(encoding="utf-8") as f:
        return yaml.safe_load(f) or {}


def _deep_merge(base: dict[str, Any], overlay: dict[str, Any]) -> dict[str, Any]:
    out = dict(base)
    for key, value in overlay.items():
        if key in out and isinstance(out[key], dict) and isinstance(value, dict):
            out[key] = _deep_merge(out[key], value)
        else:
            out[key] = value
    return out


def resolve_api_local_config_path(*, for_write: bool = False) -> Path:
    """api.local 读写路径（脑区 LLM 路由 overlay）。"""
    env_api = os.environ.get("NIANNIAN_API_CONFIG")
    if env_api:
        return Path(env_api).expanduser().resolve()
    cfg_dir = resolve_config_path().parent
    image_cfg_dir = _PKG_ROOT / "config"
    if for_write:
        if cfg_dir.resolve() != image_cfg_dir.resolve():
            return (cfg_dir / "api.local.yaml").resolve()
        return (image_cfg_dir / "api.local.yaml").resolve()
    for candidate in (
        cfg_dir / "api.local.yaml",
        image_cfg_dir / "api.local.yaml",
    ):
        if candidate.is_file():
            return candidate.resolve()
    if for_write:
        return (image_cfg_dir / "api.local.yaml").resolve()
    return (image_cfg_dir / "api.local.yaml").resolve()


def save_api_local_config(data: dict[str, Any], path: Path | None = None) -> Path:
    """写入 api.local.yaml（脑区路由 overlay）。"""
    target = path or resolve_api_local_config_path(for_write=True)
    target.parent.mkdir(parents=True, exist_ok=True)
    with target.open("w", encoding="utf-8") as f:
        yaml.safe_dump(data, f, allow_unicode=True, default_flow_style=False, sort_keys=False)
    return target


def _merge_api_local_overlay(cfg: dict[str, Any]) -> dict[str, Any]:
    overlay_path = resolve_api_local_config_path()
    if not overlay_path.is_file():
        return cfg
    overlay = _load_yaml(overlay_path)
    if overlay:
        cfg["api"] = _deep_merge(cfg.get("api") or {}, overlay)
    return cfg


def load_config(config_path: Path | None = None) -> dict[str, Any]:
    path = config_path or resolve_config_path()
    if path.resolve() == _DEFAULT_CONFIG.resolve():
        cfg = _load_yaml(path)
    elif _DEFAULT_CONFIG.is_file():
        cfg = _deep_merge(_load_yaml(_DEFAULT_CONFIG), _load_yaml(path))
    else:
        cfg = _load_yaml(path)
    cfg = _merge_api_local_overlay(cfg)
    return resolve_env_value(cfg)


def resolve_env_value(value: Any) -> Any:
    """解析 ${ENV_VAR} 占位，未设置时返回 None。"""
    if isinstance(value, str) and value.startswith("${") and value.endswith("}"):
        return os.environ.get(value[2:-1]) or None
    if isinstance(value, dict):
        return {k: resolve_env_value(v) for k, v in value.items()}
    if isinstance(value, list):
        return [resolve_env_value(v) for v in value]
    return value


def resolve_providers_config_candidates() -> list[Path]:
    """providers 查找顺序：显式 env → NIANNIAN_CONFIG 同目录 → 镜像内 config。"""
    candidates: list[Path] = []
    env_providers = os.environ.get("NIANNIAN_PROVIDERS_CONFIG")
    if env_providers:
        candidates.append(Path(env_providers).expanduser().resolve())
    cfg_path = resolve_config_path()
    cfg_dir = cfg_path.parent
    image_cfg_dir = _PKG_ROOT / "config"
    if cfg_dir.resolve() != image_cfg_dir.resolve():
        candidates.append(cfg_dir / "providers.local.yaml")
        candidates.append(cfg_dir / "providers.example.yaml")
    candidates.extend(
        [
            image_cfg_dir / "providers.local.yaml",
            image_cfg_dir / "providers.example.yaml",
        ]
    )
    # 去重保序
    seen: set[Path] = set()
    unique: list[Path] = []
    for p in candidates:
        rp = p.resolve()
        if rp not in seen:
            seen.add(rp)
            unique.append(p)
    return unique


def resolve_providers_config_path(*, for_write: bool = False) -> Path:
    """providers.local 读写路径。"""
    env_providers = os.environ.get("NIANNIAN_PROVIDERS_CONFIG")
    if env_providers:
        return Path(env_providers).expanduser().resolve()
    for p in resolve_providers_config_candidates():
        if p.is_file() and p.name != "providers.example.yaml":
            return p.resolve()
    if for_write:
        cfg_dir = resolve_config_path().parent
        if cfg_dir.resolve() != (_PKG_ROOT / "config").resolve():
            return (cfg_dir / "providers.local.yaml").resolve()
        return (_PKG_ROOT / "config" / "providers.local.yaml").resolve()
    return resolve_providers_config_candidates()[0].resolve()


def load_providers_config(path: Path | None = None) -> dict[str, Any]:
    """读取 API Key；优先 providers.local.yaml，否则 example 占位。"""
    candidates = [path] if path is not None else resolve_providers_config_candidates()
    for p in candidates:
        if p.is_file():
            with p.open(encoding="utf-8") as f:
                raw = yaml.safe_load(f) or {}
                return resolve_env_value(raw)
    return {}


def save_providers_config(data: dict[str, Any], path: Path | None = None) -> Path:
    """写入 providers.local.yaml；返回实际路径。"""
    target = path or resolve_providers_config_path(for_write=True)
    target.parent.mkdir(parents=True, exist_ok=True)
    with target.open("w", encoding="utf-8") as f:
        yaml.safe_dump(data, f, allow_unicode=True, default_flow_style=False, sort_keys=False)
    return target


def resolve_agents_config_candidates() -> list[Path]:
    """agents.local 查找顺序：env → config 同目录 → 镜像 config。"""
    candidates: list[Path] = []
    env_agents = os.environ.get("NIANNIAN_AGENTS_CONFIG")
    if env_agents:
        candidates.append(Path(env_agents).expanduser().resolve())
    cfg_path = resolve_config_path()
    cfg_dir = cfg_path.parent
    image_cfg_dir = _PKG_ROOT / "config"
    if cfg_dir.resolve() != image_cfg_dir.resolve():
        candidates.append(cfg_dir / "agents.local.yaml")
    candidates.extend(
        [
            image_cfg_dir / "agents.local.yaml",
            image_cfg_dir / "agents.local.yaml.template",
        ]
    )
    seen: set[Path] = set()
    unique: list[Path] = []
    for p in candidates:
        rp = p.resolve()
        if rp not in seen:
            seen.add(rp)
            unique.append(p)
    return unique


def resolve_agents_config_path(*, for_write: bool = False) -> Path:
    """agents.local 读写路径；for_write 时若无现成文件则落在 config 同目录。"""
    env_agents = os.environ.get("NIANNIAN_AGENTS_CONFIG")
    if env_agents:
        return Path(env_agents).expanduser().resolve()
    for p in resolve_agents_config_candidates():
        if p.is_file() and not p.name.endswith(".template"):
            return p.resolve()
    if for_write:
        cfg_dir = resolve_config_path().parent
        if cfg_dir.resolve() != (_PKG_ROOT / "config").resolve():
            return (cfg_dir / "agents.local.yaml").resolve()
        return (_PKG_ROOT / "config" / "agents.local.yaml").resolve()
    return resolve_agents_config_candidates()[0].resolve()


def load_agents_config(path: Path | None = None) -> dict[str, Any]:
    """Agent Pool / Loop 配置；无文件时返回 {}。"""
    if path is not None:
        if path.is_file() and not path.name.endswith(".template"):
            return resolve_env_value(_load_yaml(path))
        return {}
    for p in resolve_agents_config_candidates():
        if p.is_file() and not p.name.endswith(".template"):
            return resolve_env_value(_load_yaml(p))
    return {}


def save_agents_config(data: dict[str, Any], path: Path | None = None) -> Path:
    """写入 agents.local.yaml；返回实际路径。"""
    target = path or resolve_agents_config_path(for_write=True)
    target.parent.mkdir(parents=True, exist_ok=True)
    with target.open("w", encoding="utf-8") as f:
        yaml.safe_dump(data, f, allow_unicode=True, default_flow_style=False, sort_keys=False)
    return target


def resolve_data_home(override: str | Path | None = None) -> Path:
    if override is not None:
        return Path(override).expanduser().resolve()
    env = os.environ.get("NIANNIAN_HOME")
    if env:
        return Path(env).expanduser().resolve()
    cfg = load_config()
    if cfg.get("data_home"):
        return Path(cfg["data_home"]).expanduser().resolve()
    return (_PKG_ROOT / "data").resolve()
