"""Preset loader — reads JSON config files from the presets directory."""

from __future__ import annotations

import json
import logging
from pathlib import Path
from typing import Any

logger = logging.getLogger(__name__)

_PRESETS_DIR = Path(__file__).parent
_VALID_SECTIONS = {"preprocessing", "vectorization", "postprocessing"}

# In-memory cache — loaded once per process, cleared on reload().
_cache: dict[str, dict[str, Any]] = {}


def _load_all() -> dict[str, dict[str, Any]]:
    """Scan the presets directory and load every .json file."""
    presets: dict[str, dict[str, Any]] = {}
    for p in sorted(_PRESETS_DIR.glob("*.json")):
        try:
            data = json.loads(p.read_text())
            name = data.get("name", p.stem)
            presets[name] = data
        except (json.JSONDecodeError, OSError) as exc:
            logger.warning("Skipping invalid preset file %s: %s", p, exc)
    return presets


def all_presets() -> dict[str, dict[str, Any]]:
    """Return all presets, loading from disk on first call.

    Returns:
        Dict mapping preset name → full preset config dict.
    """
    if not _cache:
        _cache.update(_load_all())
    return dict(_cache)


def get_preset(name: str) -> dict[str, Any] | None:
    """Return a single preset by name, or None if not found."""
    presets = all_presets()
    return presets.get(name)


def preset_names() -> list[str]:
    """Return sorted list of available preset names."""
    return sorted(all_presets().keys())


def resolve_params(
    preset_name: str,
    user_params: dict[str, Any] | None = None,
) -> dict[str, Any]:
    """Resolve effective parameters by merging preset defaults with user overrides.

    The preset provides three sections: preprocessing, vectorization, postprocessing.
    User params are applied as a flat overlay — keys in user_params override matching
    keys from the preset at any level.

    Special keys:
    - ``mode``: if present in user_params, overrides ``vectorization.mode``.
    - ``epsilon``: if present in user_params, overrides ``postprocessing.epsilon``.

    Returns:
        Merged param dict with top-level keys for preprocessing, vectorization mode,
        vectorizer-specific params, and postprocessing epsilon.
    """
    user = user_params or {}
    preset = get_preset(preset_name)

    if preset is None:
        # No preset found — fall back to raw user params.
        return {
            "preprocessing": {},
            "vectorization_mode": user.get("mode", "potrace"),
            "vectorizer_params": {k: v for k, v in user.items() if k != "mode"},
            "postprocessing": {"epsilon": float(user.get("epsilon", 1.0))},
        }

    # -- Preprocessing: preset defaults + user overrides --
    pre = dict(preset.get("preprocessing", {}))
    for key in list(pre.keys()):
        if key in user:
            pre[key] = user[key]

    # -- Vectorization mode --
    vec_cfg = preset.get("vectorization", {})
    mode = user.get("mode", vec_cfg.get("mode", "potrace"))

    # -- Vectorizer-specific params: preset defaults + user overrides --
    vec_params = dict(vec_cfg.get(mode, vec_cfg.get("potrace", {})))
    # Apply any user overrides that match vectorizer param names.
    for key in list(vec_params.keys()):
        if key in user:
            vec_params[key] = user[key]

    # -- Postprocessing --
    post = dict(preset.get("postprocessing", {}))
    if "epsilon" in user:
        post["epsilon"] = float(user["epsilon"])

    return {
        "preprocessing": pre,
        "vectorization_mode": mode,
        "vectorizer_params": vec_params,
        "postprocessing": post,
    }


def reload() -> None:
    """Clear the preset cache and reload from disk."""
    _cache.clear()
    _cache.update(_load_all())
