"""
配置加载:default.yaml(基础)→ 用户配置(覆盖)→ CLI(覆盖)。
"""
import logging
from dataclasses import dataclass, field
from pathlib import Path
from typing import Optional, List

# ── 用户配置文件搜索路径 ──
_CONFIG_PATHS = [
    Path.home() / ".config" / "ttk" / "ttk.conf.yaml",
    Path("ttk.conf.yaml"),
]

_config: Optional[dict] = None


# ====================================================================
# 数据模型
# ====================================================================

@dataclass
class Endpoint:
    """远端 XPU 服务器端点。"""
    host: str
    port: int
    providers: Optional[List[str]] = None
    hardware: Optional[str] = None

    @classmethod
    def from_dict(cls, d: dict) -> "Endpoint":
        return cls(
            host=d["host"],
            port=d["port"],
            providers=d.get("providers"),
            hardware=d.get("hardware"),
        )


@dataclass
class RemoteConfig:
    """远端执行配置。"""
    endpoints: List[Endpoint] = field(default_factory=list)
    backoff_base_s: float = 0.5
    backoff_max_s: float = 10.0
    backoff_jitter: float = 0.25
    max_503_retries: int = 10
    max_conn_retries: int = 5
    dispatch_deadline_s: int = 300
    tls_ca: str = ""
    tls_cert: str = ""
    tls_key: str = ""
    tls_skip_verify: bool = False

    @classmethod
    def from_dict(cls, d: dict) -> "RemoteConfig":
        endpoints = [Endpoint.from_dict(ep) for ep in d.get("endpoints", []) if isinstance(ep, dict)]
        fields = {k: v for k, v in d.items() if k in cls.__dataclass_fields__ and k != "endpoints"}
        return cls(endpoints=endpoints, **fields)


def get_remote_config() -> Optional[RemoteConfig]:
    """从已加载的 _config 取 remote 段。无 endpoints 返回 None。"""
    cfg = get_config()
    remote = cfg.get("remote", {})
    if not remote.get("endpoints"):
        return None
    return RemoteConfig.from_dict(remote)


def get_hardware_config() -> dict:
    """frameworks 段;空返回 {}(仅 cpu 合法默认,区别 remote 的 None)。"""
    return get_config().get("frameworks", {}) or {}


# ====================================================================
# 加载逻辑
# ====================================================================

def deep_merge(base: dict, override: dict) -> dict:
    """递归合并配置:override 覆盖 base。

    仅 dict 类型逐层合并;list 与标量由 override 整体替换
    (配置语义:list 不做元素级合并,整体替换更可预期,避免静默拼凑)。
    """
    merged = dict(base)
    for k, v in override.items():
        cur = merged.get(k)
        if isinstance(cur, dict) and isinstance(v, dict):
            merged[k] = deep_merge(cur, v)
        else:
            merged[k] = v
    return merged


def _load_yaml(path) -> dict:
    """加载 YAML 文件,返回 dict。文件不存在返回 {};格式错抛 yaml.YAMLError(fail-loud)。"""
    import yaml

    p = Path(path)
    if not p.exists():
        return {}
    return yaml.safe_load(p.read_text("utf-8")) or {}


def _default_config_path() -> Path:
    """默认配置文件路径(包内 default.yaml)。"""
    return Path(__file__).parent / "default.yaml"


def load_config(config_path: str = None) -> dict:
    """加载配置:default.yaml → 用户文件 → CLI 覆盖。

    Args:
        config_path: 可选 CLI 指定的配置文件路径

    Returns:
        合并后的配置 dict
    """
    global _config

    # 1. 基础:包内 default.yaml
    cfg = _load_yaml(_default_config_path())

    # 2. 覆盖:用户配置文件(按优先级)
    for path in _CONFIG_PATHS:
        if path.exists():
            logging.debug(f"Loading config: {path}")
            cfg = deep_merge(cfg, _load_yaml(path))

    # 3. 覆盖:CLI 指定的配置文件
    if config_path:
        cfg = deep_merge(cfg, _load_yaml(config_path))

    _config = cfg
    return _config


def get_config() -> dict:
    """返回已加载的配置。_config is None 时 raise(调用方应先 load_config)。

    删 lazy fallback:原行为是未加载时静默 load_config()(丢 --config),
    掩盖了 forkserver worker 拿不到配置的 bug。改为显式 raise 让问题早暴露。
    """
    global _config
    if _config is None:
        raise RuntimeError("config not loaded — call load_config() first")
    return _config