"""Config layering — Approach C hybrid strategy.

Loads code defaults from ``src/vastian/seed/config/`` and deep-merges with
user overrides from the user's config directory (BYOS or local data root).

Usage::

    from vastian.config_loader import load_config

    # Returns merged dict: seed defaults + user overrides
    sessions = load_config("sessions.yaml")

    # Nested configs work too
    domains = load_config("scenarios/domains.yaml")

For cloud users the *user_config_dir* comes from the hydrated ephemeral
data root.  For desktop users it resolves via ``get_config_dir()``.
"""

from __future__ import annotations

import copy
import logging
from pathlib import Path
from typing import Any

import yaml

from vastian.seed import get_seed_path

logger = logging.getLogger(__name__)


def _deep_merge(base: dict[str, Any], override: dict[str, Any]) -> dict[str, Any]:
    """Recursively merge *override* into a copy of *base*.

    - Dicts are merged recursively.
    - All other types (lists, scalars) in *override* replace *base* values.
    - Keys only in *base* are preserved.
    """
    merged = copy.deepcopy(base)
    for key, val in override.items():
        if key in merged and isinstance(merged[key], dict) and isinstance(val, dict):
            merged[key] = _deep_merge(merged[key], val)
        else:
            merged[key] = copy.deepcopy(val)
    return merged


def load_config(
    rel_path: str,
    user_config_dir: Path | None = None,
) -> dict[str, Any]:
    """Load a YAML config with layering: seed defaults + user overrides.

    Parameters
    ----------
    rel_path:
        Relative path within the config directory, e.g. ``"sessions.yaml"``
        or ``"scenarios/domains.yaml"``.
    user_config_dir:
        Explicit user config directory.  When *None*, resolves via
        ``get_config_dir()`` (the normal desktop path).

    Returns
    -------
    dict
        Merged config dict.  Returns ``{}`` if neither seed nor user file
        exists.
    """
    # 1. Load seed defaults (shipped with the app)
    seed_path = get_seed_path("config", *rel_path.replace("\\", "/").split("/"))
    base: dict[str, Any] = _load_yaml(seed_path)

    # 2. Load user overrides
    if user_config_dir is None:
        user_config_dir = _default_user_config_dir()
    user_path = user_config_dir / rel_path.replace("\\", "/")
    overrides: dict[str, Any] = _load_yaml(user_path)

    # 3. Merge
    if not base and not overrides:
        return {}
    if not base:
        return overrides
    if not overrides:
        return base
    return _deep_merge(base, overrides)


def load_config_list(
    rel_path: str,
    user_config_dir: Path | None = None,
) -> list[Any]:
    """Load a YAML config that is a list at top level.

    For lists the user file *replaces* the seed entirely (no merge).
    If the user file doesn't exist, the seed list is returned.
    """
    if user_config_dir is None:
        user_config_dir = _default_user_config_dir()
    user_path = user_config_dir / rel_path.replace("\\", "/")
    user_data = _load_yaml_raw(user_path)
    if isinstance(user_data, list):
        return user_data

    seed_path = get_seed_path("config", *rel_path.replace("\\", "/").split("/"))
    seed_data = _load_yaml_raw(seed_path)
    return seed_data if isinstance(seed_data, list) else []


# ── Internals ────────────────────────────────────────────────────


def _load_yaml(path: Path) -> dict[str, Any]:
    """Load a YAML file, returning ``{}`` if missing or not a dict."""
    if not path.exists():
        return {}
    try:
        data = yaml.safe_load(path.read_text(encoding="utf-8"))
        return data if isinstance(data, dict) else {}
    except Exception:
        logger.warning("Failed to load YAML: %s", path, exc_info=True)
        return {}


def _load_yaml_raw(path: Path) -> Any:
    """Load a YAML file returning the raw parsed value."""
    if not path.exists():
        return None
    try:
        return yaml.safe_load(path.read_text(encoding="utf-8"))
    except Exception:
        logger.warning("Failed to load YAML: %s", path, exc_info=True)
        return None


def _default_user_config_dir() -> Path:
    """Resolve the user config dir via data root (for desktop / single-user mode)."""
    from vastian.storage.data_root import get_config_dir

    return get_config_dir()
