"""Schema registry loader — populates field/function registries at import time.

If octorules-wirefilter is installed, builds the registry dynamically from
wirefilter's get_schema_info() merged with overlay.toml metadata (phase
restrictions, plan requirements, is_response flags).

If wirefilter is not available, falls back to schemas.json — a frozen
snapshot generated by scripts/sync_schemas.py.
"""

from __future__ import annotations

import json
import logging
from pathlib import Path

log = logging.getLogger(__name__)

_SCHEMAS_DIR = Path(__file__).resolve().parent
_OVERLAY_PATH = _SCHEMAS_DIR / "overlay.toml"
_FALLBACK_PATH = _SCHEMAS_DIR / "schemas.json"


def _load_overlay() -> dict:
    try:
        import tomllib
    except ModuleNotFoundError:
        import tomli as tomllib  # type: ignore[no-redef]
    with open(_OVERLAY_PATH, "rb") as f:
        return tomllib.load(f)


def _build_from_wirefilter() -> dict | None:
    """Try to build schema data from wirefilter + overlay. Returns None if unavailable."""
    try:
        from octorules_wirefilter import get_schema_info
    except ImportError:
        return None

    schema = get_schema_info()
    overlay = _load_overlay()
    field_overlay = overlay.get("fields", {})
    func_overlay = overlay.get("functions", {})

    fields = []
    for entry in schema["fields"]:
        name = entry["name"]
        meta = field_overlay.get(name, {})
        f: dict = {"name": name, "type": entry["type"]}
        if meta.get("requires_plan"):
            f["requires_plan"] = meta["requires_plan"]
        if meta.get("is_response"):
            f["is_response"] = True
        fields.append(f)

    all_func_names = list(schema["functions"])
    for name in func_overlay:
        if name not in all_func_names:
            all_func_names.append(name)

    functions = []
    for name in all_func_names:
        meta = func_overlay.get(name, {})
        f = {"name": name}
        if meta.get("restricted_phases"):
            f["restricted_phases"] = sorted(meta["restricted_phases"])
        if meta.get("requires_plan"):
            f["requires_plan"] = meta["requires_plan"]
        functions.append(f)

    return {"fields": fields, "functions": functions}


def _load_fallback() -> dict:
    """Load the frozen schema snapshot from schemas.json.

    TODO(v1.0.0): Consider removing the no-wirefilter fallback entirely.
    Without wirefilter, the linter already has degraded coverage (no type
    checking, no syntax validation). Dropping the fallback would mean
    registry-dependent rules (unknown field, plan tier, phase restrictions)
    are also skipped — which is honest. This would eliminate schemas.json,
    sync_schemas.py, and the pre-commit hook. See docs/schemas.md.
    """
    with open(_FALLBACK_PATH) as f:
        return json.load(f)


def load_schema() -> dict:
    """Load schema data, preferring wirefilter if available."""
    data = _build_from_wirefilter()
    if data is not None:
        log.debug("Schema loaded from wirefilter + overlay.toml")
        return data
    log.debug("wirefilter not installed, using frozen schemas.json fallback")
    return _load_fallback()


def load_managed_lists() -> frozenset[str]:
    """Load valid Cloudflare managed list names from overlay.toml."""
    overlay = _load_overlay()
    ml = overlay.get("managed_lists", {})
    # New format: kinds = {name: kind}
    kinds = ml.get("kinds", {})
    if kinds:
        return frozenset(kinds.keys())
    # Legacy format: names = [...]
    return frozenset(ml.get("names", []))


def load_managed_list_kinds() -> dict[str, str]:
    """Load managed list name → kind mapping from overlay.toml."""
    overlay = _load_overlay()
    return dict(overlay.get("managed_lists", {}).get("kinds", {}))
