"""Centralized configuration manager for SalmAlm.

중앙집중 설정 관리자 — 단일 진실 소스(Single Source of Truth).
우선순위: CLI args > 환경변수 > config file > constants.py defaults
"""

import json
import os
from salmalm.constants import DATA_DIR


# Runtime CLI overrides (populated by __main__ / entry points)
_cli_overrides: dict = {}


def set_cli_overrides(overrides: dict) -> None:
    """Set CLI argument overrides (called at startup)."""
    _cli_overrides.update(overrides)


class ConfigManager:
    """중앙집중 설정 관리자.

    Resolution order for ``resolve()``:
      1. CLI args (``_cli_overrides``)
      2. Environment variables (``SALMALM_<NAME>_<KEY>``)
      3. Config file (``~/.salmalm/<name>.json``)
      4. Caller-supplied *defaults* (typically from ``constants.py``)
    """

    BASE_DIR = DATA_DIR

    @classmethod
    def load(cls, name: str, defaults: dict = None) -> dict:
        """설정 파일 로드. name='mood' → ~/.salmalm/mood.json"""
        path = cls.BASE_DIR / f"{name}.json"
        if path.exists():
            try:
                with open(path, encoding="utf-8") as f:
                    config = json.load(f)
                if defaults:
                    merged = {**defaults, **config}
                    return merged
                return config
            except (json.JSONDecodeError, OSError):
                pass
        return dict(defaults) if defaults else {}

    @classmethod
    def save(cls, name: str, config: dict) -> None:
        """설정 파일 저장."""
        cls.BASE_DIR.mkdir(parents=True, exist_ok=True)
        path = cls.BASE_DIR / f"{name}.json"
        with open(path, "w", encoding="utf-8") as f:
            json.dump(config, f, indent=2, ensure_ascii=False)

    @classmethod
    def resolve(cls, name: str, key: str, default=None):
        """Resolve a single config value with full priority chain.

        1. CLI args  2. Env var ``SALMALM_<NAME>_<KEY>``  3. Config file  4. *default*
        """
        # 1. CLI overrides
        cli_key = f"{name}.{key}"
        if cli_key in _cli_overrides:
            return _cli_overrides[cli_key]

        # 2. Environment variable
        env_key = f"SALMALM_{name.upper()}_{key.upper()}"
        env_val = os.environ.get(env_key)
        if env_val is not None:
            # Attempt JSON parse for non-string types
            try:
                return json.loads(env_val)
            except (json.JSONDecodeError, ValueError):
                return env_val

        # 3. Config file
        config = cls.load(name)
        if key in config:
            return config[key]

        # 4. Caller-supplied default (constants.py value)
        return default

    @classmethod
    def get(cls, name: str, key: str, default=None):
        """단일 키 조회 (resolve 사용)."""
        return cls.resolve(name, key, default)

    @classmethod
    def set(cls, name: str, key: str, value: str) -> None:
        """단일 키 설정."""
        config = cls.load(name)
        config[key] = value
        cls.save(name, config)

    @classmethod
    def exists(cls, name: str) -> bool:
        """Exists."""
        return (cls.BASE_DIR / f"{name}.json").exists()

    @classmethod
    def delete(cls, name: str) -> bool:
        """Delete."""
        path = cls.BASE_DIR / f"{name}.json"
        if path.exists():
            path.unlink()
            return True
        return False

    @classmethod
    def list_configs(cls) -> list:
        """모든 설정 파일 목록."""
        if not cls.BASE_DIR.exists():
            return []
        return [f.stem for f in cls.BASE_DIR.glob("*.json")]

    @classmethod
    def migrate(cls, name: str) -> bool:
        """설정 파일 마이그레이션 실행."""
        config = cls.load(name)
        current_version = config.get("_version", 0)
        migrated = False
        for migration in CONFIG_MIGRATIONS:
            if migration["version"] > current_version:
                config = migration["migrate"](config)
                config["_version"] = migration["version"]
                migrated = True
        if migrated:
            cls.save(name, config)
        return migrated


# ── Config Migrations ──


def _migrate_v1(config: dict) -> dict:
    """routing.json → channels.json 통합."""
    # If routing keys exist at top level, nest them under byChannel
    if "simple" in config or "moderate" in config or "complex" in config:
        routing = {}
        for k in ("simple", "moderate", "complex"):
            if k in config:
                routing[k] = config.pop(k)
        config.setdefault("routing", routing)
    return config


def _migrate_v2(config: dict) -> dict:
    """heartbeat.json active_hours 추가."""
    # Ensure active_hours defaults exist
    config.setdefault("active_hours", {"start": "08:00", "end": "24:00"})
    config.setdefault("timezone", "Asia/Seoul")
    return config


CONFIG_MIGRATIONS = [
    {
        "version": 1,
        "description": "routing.json → channels.json 통합",
        "migrate": _migrate_v1,
    },
    {
        "version": 2,
        "description": "heartbeat.json active_hours 추가",
        "migrate": _migrate_v2,
    },
]
