from __future__ import annotations

import json
from pathlib import Path


CATALOG_PATH = Path.home() / ".codex" / "models_cache.json"

MODEL_ALIASES = {
    "sol": "gpt-5.6-sol",
    "terra": "gpt-5.6-terra",
    "luna": "gpt-5.6-luna",
}

# Used only when the catalog file is unreadable. Codex itself refuses an
# unsupported effort, so a stale entry here costs a round trip, never a wrong run.
FALLBACK_EFFORTS: dict[str, tuple[str, tuple[str, ...]]] = {
    "gpt-5.6-sol": ("low", ("low", "medium", "high", "xhigh", "max", "ultra")),
    "gpt-5.6-terra": ("medium", ("low", "medium", "high", "xhigh", "max", "ultra")),
    "gpt-5.6-luna": ("medium", ("low", "medium", "high", "xhigh", "max")),
}

EFFORT_ALIASES = {"med": "medium", "pro": "ultra"}


class ModelCatalog:
    def __init__(self, path: Path | None = None) -> None:
        self._entries = _load(CATALOG_PATH if path is None else Path(path))

    def known(self, model: str) -> bool:
        return model in self._entries

    def models(self) -> tuple[str, ...]:
        return tuple(sorted(self._entries))

    def supported_efforts(self, model: str) -> tuple[str, ...]:
        entry = self._entries.get(model)
        return entry[1] if entry else ()

    def default_effort(self, model: str) -> str | None:
        entry = self._entries.get(model)
        return entry[0] if entry else None

    def resolve_effort(self, model: str, effort: str) -> str:
        """Canonicalize `effort` for `model`, or raise ValueError naming what fits.

        `pro` resolves to the highest tier the model actually offers, so a caller
        asking for the best available never has to know the tier names.
        """
        supported = self.supported_efforts(model)
        if not supported:
            raise ValueError(f"unknown model '{model}'")
        canonical = EFFORT_ALIASES.get(effort, effort)
        if canonical == "ultra" and canonical not in supported:
            canonical = supported[-1]
        if canonical not in supported:
            raise ValueError(
                f"unsupported reasoning effort '{effort}' for model '{model}' "
                f"(supported: {', '.join(supported)})"
            )
        return canonical


def _load(path: Path) -> dict[str, tuple[str, tuple[str, ...]]]:
    try:
        raw = json.loads(path.read_text(encoding="utf-8"))
    except (OSError, json.JSONDecodeError):
        return dict(FALLBACK_EFFORTS)

    entries: dict[str, tuple[str, tuple[str, ...]]] = {}
    for model in raw.get("models") or []:
        if not isinstance(model, dict):
            continue
        slug = model.get("slug")
        levels = model.get("supported_reasoning_levels") or []
        efforts = tuple(
            level["effort"]
            for level in levels
            if isinstance(level, dict) and isinstance(level.get("effort"), str)
        )
        if not isinstance(slug, str) or not efforts:
            continue
        default = model.get("default_reasoning_level")
        entries[slug] = (default if default in efforts else efforts[0], efforts)
    return entries or dict(FALLBACK_EFFORTS)
