#!/usr/bin/env python3
"""Provider/model catalog loading, overlays, and semantic validation."""

from __future__ import annotations

import math
import re
from collections.abc import Mapping
from pathlib import Path
from typing import Any
from urllib.parse import urlsplit

from mmo_util import (
    config_root,
    deep_merge,
    install_root,
    read_toml,
    valid_absolute_uri,
    valid_http_header_value,
    validate_id,
)
from mmo_version import MMO_SCHEMA_VERSION

ALLOWED_DRIVERS = {
    "switchyard",
    "codex_builtin",
    "codex_custom",
    "codex_oss",
    "catalog_only",
}
ALLOWED_WIRE_PROTOCOLS = {
    "openai_chat",
    "openai_responses",
    "anthropic_messages",
    "codex_builtin",
    "codex_oss",
    "catalog_only",
}
ALLOWED_BILLING_MODES = {"api", "subscription", "chatgpt_subscription", "local", "catalog_only"}
ALLOWED_MODEL_KINDS = {
    "chat",
    "vision_chat",
    "ocr",
    "image_generation",
    "video_generation",
    "audio_transcription",
    "agent_service",
}
ALLOWED_MODALITIES = {"text", "image", "audio", "video", "file"}
ALLOWED_REASONING = {"none", "minimal", "low", "medium", "high", "xhigh", "max", "ultra"}


def user_catalog_root() -> Path:
    return config_root() / "catalog.d"


ROUTE_FIELDS = {
    "driver",
    "name",
    "api_operator",
    "access_product",
    "wire_protocol",
    "billing_mode",
    "base_url",
    "provider_id",
    "auth",
    "auth_link_mode",
    "wire_api",
    "credential_envs",
    "http_headers",
    "env_http_headers",
    "extra_headers",
    "request_max_retries",
    "stream_max_retries",
    "stream_idle_timeout_ms",
    "max_retries",
    "transport_modalities",
    "transport_output_modalities",
    "tool_calling",
    "parallel_tool_calls",
    "preserves_tool_media",
    "tool_result_modalities",
    "supports_documents",
    "resource_group",
    "inventory",
    "openrouter_policy",
}
ROUTE_COMMON_FIELDS = {
    "driver",
    "name",
    "api_operator",
    "access_product",
    "wire_protocol",
    "billing_mode",
    "transport_modalities",
    "transport_output_modalities",
    "tool_calling",
    "parallel_tool_calls",
    "preserves_tool_media",
    "tool_result_modalities",
    "supports_documents",
    "resource_group",
    "inventory",
    "openrouter_policy",
}
ROUTE_DRIVER_FIELDS = {
    "switchyard": {
        "base_url",
        "credential_envs",
        "extra_headers",
        "max_retries",
    },
    "codex_custom": {
        "base_url",
        "wire_api",
        "credential_envs",
        "http_headers",
        "env_http_headers",
        "request_max_retries",
        "stream_max_retries",
        "stream_idle_timeout_ms",
    },
    "codex_builtin": {"provider_id", "auth", "auth_link_mode"},
    "codex_oss": {"provider_id"},
    # Catalog-only providers retain endpoint/auth facts for inventory and
    # operator diagnostics, but none of these fields is emitted for execution.
    "catalog_only": {
        "base_url",
        "wire_api",
        "credential_envs",
        "auth",
    },
}
MODEL_FIELDS = {
    "maker",
    "route",
    "upstream_id",
    "display_name",
    "description",
    "kind",
    "agent_compatible",
    "context_window",
    "max_output_tokens",
    "reasoning_levels",
    "default_reasoning",
    "modalities",
    "output_modalities",
    "supports_tool_images",
    "supports_documents",
    "tool_calling",
    "supports_custom_tools",
    "parallel_tool_calls",
    "supports_reasoning_summaries",
    "structured_output",
    "availability",
    "capability_confidence",
    "source",
    "availability_source",
    "capability_source",
    "pricing_source",
    "inventory",
    "resource_group",
    "input_cost_per_million",
    "cached_input_cost_per_million",
    "cache_write_input_cost_per_million",
    "output_cost_per_million",
    "unit_cost_usd",
    "extra_body",
    "route_policy",
}
OPENROUTER_POLICY_FIELDS = {
    "only",
    "order",
    "allow_fallbacks",
    "require_parameters",
    "data_collection",
    "zdr",
    "quantizations",
    "sort",
    "max_price",
}
RESOURCE_FIELDS = {"description", "lock_key", "max_active"}


def _reject_unknown_fields(value: Mapping[str, Any], allowed: set[str], label: str) -> None:
    unknown = sorted(set(value) - allowed)
    if unknown:
        raise ValueError(f"{label} has unknown fields: {', '.join(unknown)}")


def _schema_version(value: Any, expected: int, label: str) -> int:
    if not isinstance(value, int) or isinstance(value, bool) or value != expected:
        raise ValueError(f"unsupported {label} schema_version")
    return value


def _nonempty_string(value: Any, label: str) -> str:
    if not isinstance(value, str) or not value.strip():
        raise ValueError(f"{label} must be a non-empty string")
    return value


def _optional_nonempty_string(value: Any, label: str) -> str | None:
    if value is None:
        return None
    return _nonempty_string(value, label)


def _enum_string(value: Any, allowed: set[str], label: str) -> str:
    if not isinstance(value, str) or value not in allowed:
        raise ValueError(f"{label} must be one of {sorted(allowed)}")
    return value


def _load_catalog_fragment(path: Path) -> dict[str, Any]:
    data = read_toml(path)
    _reject_unknown_fields(
        data, {"schema_version", "routes", "models", "resources"}, f"catalog {path}"
    )
    try:
        _schema_version(data.get("schema_version"), MMO_SCHEMA_VERSION, "catalog")
    except ValueError as exc:
        raise ValueError(f"unsupported catalog schema_version in {path}") from exc
    result: dict[str, Any] = {}
    for section in ("routes", "models", "resources"):
        value = data.get(section, {})
        if not isinstance(value, Mapping):
            raise ValueError(f"catalog {section!r} must be a table in {path}")
        if not all(isinstance(item, Mapping) for item in value.values()):
            raise ValueError(f"catalog {section!r} entries must be tables in {path}")
        result[section] = value
    return result


def load_global_catalog() -> dict[str, Any]:
    """Load the bundled catalog plus deterministic user overlays.

    User fragments are merged lexicographically so an operator can override or
    extend a provider/model without editing installed files. Profile-local
    fragments are applied separately by :func:`load_catalog`.
    """

    catalog = _load_catalog_fragment(install_root() / "config" / "catalog.toml")
    root = user_catalog_root()
    if root.is_dir():
        for path in sorted(root.glob("*.toml")):
            catalog = deep_merge(catalog, _load_catalog_fragment(path))
    return catalog


def validate_catalog_data(catalog: Mapping[str, Any], *, label: str = "catalog") -> dict[str, Any]:
    """Validate one fully merged catalog and all cross-section references."""

    if not isinstance(catalog, Mapping):
        raise ValueError(f"{label} must be a table")
    _reject_unknown_fields(catalog, {"schema_version", "routes", "models", "resources"}, label)
    if "schema_version" in catalog:
        _schema_version(catalog["schema_version"], MMO_SCHEMA_VERSION, label)
    sections: dict[str, Mapping[str, Any]] = {}
    for section in ("routes", "models", "resources"):
        value = catalog.get(section, {})
        if not isinstance(value, Mapping):
            raise ValueError(f"{label}.{section} must be a table")
        if not all(isinstance(item, Mapping) for item in value.values()):
            raise ValueError(f"{label}.{section} entries must be tables")
        sections[section] = value
    routes = {key: _validate_route(key, value) for key, value in sections["routes"].items()}
    resources = _validate_resources(sections["resources"])
    models = _validate_models(sections["models"], routes)
    for key, route in routes.items():
        resource = route.get("resource_group")
        if resource is not None and resource not in resources:
            raise ValueError(f"route {key}: unknown resource group {resource!r}")
    for key, model in models.items():
        resource = model.get("resource_group")
        if resource is not None and resource not in resources:
            raise ValueError(f"model {key}: unknown resource group {resource!r}")
    return {"routes": routes, "models": models, "resources": resources}


def validated_global_catalog() -> dict[str, Any]:
    """Return the merged global catalog after full semantic validation."""

    return validate_catalog_data(load_global_catalog(), label="global catalog")


def load_catalog(profile_dir: Path, profile_data: Mapping[str, Any]) -> dict[str, Any]:
    catalog = load_global_catalog()
    profile_catalog = profile_data.get("catalog", "catalog.toml")
    if not isinstance(profile_catalog, str) or not profile_catalog:
        raise ValueError("profile.catalog must be a non-empty relative path string")
    if Path(profile_catalog).is_absolute():
        raise ValueError("profile.catalog must be a non-empty relative path string")
    path = (profile_dir / profile_catalog).resolve()
    if profile_dir not in path.parents:
        raise ValueError("profile catalog path escapes the profile pack")
    if path.is_file():
        catalog = deep_merge(catalog, _load_catalog_fragment(path))
    elif "catalog" in profile_data:
        raise ValueError(f"explicit profile catalog file is missing: {profile_catalog}")
    return catalog


def _string_list(value: Any, label: str, *, allow_empty: bool = True) -> list[str]:
    if value is None and allow_empty:
        return []
    if not isinstance(value, list) or not all(isinstance(item, str) for item in value):
        raise ValueError(f"{label} must be an array of strings")
    if any(not item.strip() for item in value):
        raise ValueError(f"{label} cannot contain empty strings")
    if not allow_empty and not value:
        raise ValueError(f"{label} cannot be empty")
    if len(value) != len(set(value)):
        raise ValueError(f"{label} cannot contain duplicates")
    return list(value)


def _positive_int(value: Any, label: str, minimum: int = 1, maximum: int = 1_000_000) -> int:
    if not isinstance(value, int) or isinstance(value, bool) or not minimum <= value <= maximum:
        raise ValueError(f"{label} must be an integer between {minimum} and {maximum}")
    return value


_HEADER_NAME = re.compile(r"[!#$%&'*+.^_`|~0-9A-Za-z-]+")
_ENVIRONMENT_NAME = re.compile(r"[A-Za-z_][A-Za-z0-9_]*")


def _validate_http_base_url(value: Any, label: str) -> str:
    """Validate an HTTP API root before downstream endpoint concatenation."""

    if not isinstance(value, str) or not valid_absolute_uri(value):
        raise ValueError(f"{label}: valid HTTP(S) base_url is required")
    try:
        parsed = urlsplit(value)
        # Accessing ``port`` performs the range and syntax checks that
        # ``urlsplit`` intentionally defers.
        _parsed_port = parsed.port
    except ValueError as exc:
        raise ValueError(f"{label}: valid HTTP(S) base_url is required") from exc
    if (
        parsed.scheme.lower() not in {"http", "https"}
        or parsed.hostname is None
        or parsed.username is not None
        or parsed.password is not None
        # Codex 0.149 trims slashes before appending the endpoint path, while
        # Switchyard 0.2.0 also normalizes known endpoint suffixes.  Neither
        # treats a query or fragment embedded in the base as a URI join input.
        or "?" in value
        or "#" in value
    ):
        raise ValueError(f"{label}: valid HTTP(S) base_url is required")
    return value


def _header_map(value: Any, label: str, *, environment_values: bool = False) -> dict[str, str]:
    if not isinstance(value, Mapping):
        raise ValueError(f"{label} must be a string-to-string table")
    result: dict[str, str] = {}
    seen: set[str] = set()
    for raw_name, raw_value in value.items():
        if not isinstance(raw_name, str) or not _HEADER_NAME.fullmatch(raw_name):
            raise ValueError(f"{label}: invalid HTTP header name {raw_name!r}")
        normalized_name = raw_name.lower()
        if normalized_name in seen:
            raise ValueError(f"{label}: duplicate case-insensitive HTTP header name {raw_name!r}")
        seen.add(normalized_name)
        if not isinstance(raw_value, str):
            raise ValueError(f"{label}.{raw_name} must be a string")
        if environment_values:
            if not _ENVIRONMENT_NAME.fullmatch(raw_value):
                raise ValueError(
                    f"{label}.{raw_name}: invalid environment variable name {raw_value!r}"
                )
        elif not valid_http_header_value(raw_value):
            raise ValueError(f"{label}.{raw_name} contains a prohibited control character")
        result[raw_name] = raw_value
    return result


def _json_compatible_value(value: Any, label: str) -> Any:
    """Validate a value destined for Switchyard's serde_json::Value map."""

    if isinstance(value, (str, bool)):
        return value
    if isinstance(value, int) and not isinstance(value, bool):
        if -(2**63) <= value <= 2**63 - 1:
            return value
        raise ValueError(f"{label} integer is outside the TOML/JSON target range")
    if isinstance(value, float):
        if math.isfinite(value):
            return value
        raise ValueError(f"{label} must not contain a non-finite number")
    if isinstance(value, list):
        return [
            _json_compatible_value(item, f"{label}[{index}]") for index, item in enumerate(value)
        ]
    if isinstance(value, Mapping):
        result: dict[str, Any] = {}
        for key, item in value.items():
            if not isinstance(key, str):
                raise ValueError(f"{label} object keys must be strings")
            result[key] = _json_compatible_value(item, f"{label}.{key}")
        return result
    raise ValueError(f"{label} must contain only JSON-compatible TOML values")


def _credential_envs(route: Mapping[str, Any], label: str) -> list[str]:
    """Validate a route's ordered credential environment names."""

    raw = route.get("credential_envs", [])
    values = _string_list(raw, f"{label}.credential_envs")
    for value in values:
        if not re.fullmatch(r"[A-Za-z_][A-Za-z0-9_]*", value):
            raise ValueError(f"{label}: invalid credential environment variable {value!r}")
    return list(dict.fromkeys(values))


def _validate_openrouter_policy(value: Any, label: str) -> dict[str, Any] | None:
    if value is None:
        return None
    if not isinstance(value, Mapping):
        raise ValueError(f"{label} must be a table")
    _reject_unknown_fields(value, OPENROUTER_POLICY_FIELDS, label)
    result = dict(value)
    for field in ("only", "order", "quantizations"):
        if field in result:
            result[field] = _string_list(result[field], f"{label}.{field}", allow_empty=False)
    for field in ("allow_fallbacks", "require_parameters", "zdr"):
        if field in result and not isinstance(result[field], bool):
            raise ValueError(f"{label}.{field} must be boolean")
    if "data_collection" in result:
        result["data_collection"] = _enum_string(
            result["data_collection"], {"allow", "deny"}, f"{label}.data_collection"
        )
    if "sort" in result:
        result["sort"] = _enum_string(
            result["sort"], {"price", "throughput", "latency"}, f"{label}.sort"
        )
    if "max_price" in result:
        price = result["max_price"]
        if not isinstance(price, Mapping):
            raise ValueError(f"{label}.max_price must be a table")
        unknown = sorted(set(price) - {"prompt", "completion", "request", "image"})
        if unknown:
            raise ValueError(f"{label}.max_price has unknown fields: {', '.join(unknown)}")
        normalized: dict[str, float] = {}
        for field, raw in price.items():
            if not isinstance(raw, (int, float)) or isinstance(raw, bool) or raw < 0:
                raise ValueError(f"{label}.max_price.{field} must be non-negative and finite")
            try:
                numeric = float(raw)
            except OverflowError:
                raise ValueError(
                    f"{label}.max_price.{field} must be non-negative and finite"
                ) from None
            if not math.isfinite(numeric):
                raise ValueError(f"{label}.max_price.{field} must be non-negative and finite")
            normalized[str(field)] = numeric
        result["max_price"] = normalized
    return result


def _validate_route(key: str, route: Mapping[str, Any]) -> dict[str, Any]:
    validate_id(key, "route id")
    _reject_unknown_fields(route, ROUTE_FIELDS, f"route {key}")
    driver = _enum_string(route.get("driver"), ALLOWED_DRIVERS, f"route {key}.driver")
    unsupported = sorted(set(route) - ROUTE_COMMON_FIELDS - ROUTE_DRIVER_FIELDS[driver])
    if unsupported:
        raise ValueError(f"route {key}: fields unsupported by driver {driver!r}: {unsupported}")
    result = dict(route)
    result.setdefault("name", key)
    result["name"] = _nonempty_string(result["name"], f"route {key}.name")
    for field in ("api_operator", "access_product"):
        result[field] = _nonempty_string(result.get(field), f"route {key}.{field}")
    result["wire_protocol"] = _enum_string(
        result.get("wire_protocol"), ALLOWED_WIRE_PROTOCOLS, f"route {key}.wire_protocol"
    )
    result["billing_mode"] = _enum_string(
        result.get("billing_mode"), ALLOWED_BILLING_MODES, f"route {key}.billing_mode"
    )
    expected_protocols = {
        "switchyard": {"openai_chat", "openai_responses", "anthropic_messages"},
        "codex_custom": {"openai_responses"},
        "codex_builtin": {"codex_builtin"},
        "codex_oss": {"codex_oss"},
        "catalog_only": {"catalog_only", "openai_chat", "openai_responses", "anthropic_messages"},
    }[driver]
    if result["wire_protocol"] not in expected_protocols:
        raise ValueError(f"route {key}.wire_protocol is incompatible with driver {driver!r}")
    result["openrouter_policy"] = _validate_openrouter_policy(
        result.get("openrouter_policy"), f"route {key}.openrouter_policy"
    )
    if result["openrouter_policy"] is not None and result["api_operator"] != "openrouter":
        raise ValueError(f"route {key}: openrouter_policy requires api_operator='openrouter'")
    result.setdefault("transport_modalities", ["text"])
    result["transport_modalities"] = _string_list(
        result["transport_modalities"], f"route {key}.transport_modalities", allow_empty=False
    )
    unknown_modalities = sorted(set(result["transport_modalities"]) - ALLOWED_MODALITIES)
    if unknown_modalities:
        raise ValueError(f"route {key}: invalid transport modalities {unknown_modalities}")
    result.setdefault(
        "transport_output_modalities",
        ["text", "image", "video", "audio", "file"] if driver == "catalog_only" else ["text"],
    )
    result["transport_output_modalities"] = _string_list(
        result["transport_output_modalities"],
        f"route {key}.transport_output_modalities",
        allow_empty=False,
    )
    unknown_output_modalities = sorted(
        set(result["transport_output_modalities"]) - ALLOWED_MODALITIES
    )
    if unknown_output_modalities:
        raise ValueError(f"route {key}: invalid output modalities {unknown_output_modalities}")
    result.setdefault("tool_calling", driver != "catalog_only")
    result.setdefault("parallel_tool_calls", driver != "catalog_only")
    result.setdefault(
        "preserves_tool_media",
        driver in {"codex_builtin", "codex_custom"},
    )
    result.setdefault(
        "tool_result_modalities",
        list(result["transport_modalities"]) if result["preserves_tool_media"] else ["text"],
    )
    result["tool_result_modalities"] = _string_list(
        result["tool_result_modalities"],
        f"route {key}.tool_result_modalities",
        allow_empty=False,
    )
    unknown_tool_modalities = sorted(set(result["tool_result_modalities"]) - ALLOWED_MODALITIES)
    if unknown_tool_modalities:
        raise ValueError(f"route {key}: invalid tool-result modalities {unknown_tool_modalities}")
    result.setdefault("supports_documents", "file" in result["transport_modalities"])
    for boolean_field in (
        "tool_calling",
        "parallel_tool_calls",
        "preserves_tool_media",
        "supports_documents",
    ):
        if not isinstance(result[boolean_field], bool):
            raise ValueError(f"route {key}.{boolean_field} must be boolean")
    if result["parallel_tool_calls"] and not result["tool_calling"]:
        raise ValueError(f"route {key}: parallel_tool_calls requires tool_calling")
    if not result["preserves_tool_media"] and set(result["tool_result_modalities"]) - {"text"}:
        raise ValueError(
            f"route {key}: non-text tool_result_modalities require preserves_tool_media=true"
        )
    result.setdefault("resource_group", None)
    result.setdefault("inventory", None)
    for field in ("resource_group", "inventory"):
        result[field] = _optional_nonempty_string(result[field], f"route {key}.{field}")
    credentials = _credential_envs(result, f"route {key}")
    result["credential_envs"] = credentials
    if driver == "switchyard":
        if "extra_headers" in result:
            result["extra_headers"] = _header_map(
                result["extra_headers"], f"route {key}.extra_headers"
            )
        result["base_url"] = _validate_http_base_url(result.get("base_url"), f"route {key}")
        result.setdefault("max_retries", 1)
        result["max_retries"] = _positive_int(
            result["max_retries"], f"route {key}.max_retries", 0, 10
        )
    elif driver == "codex_custom":
        if "http_headers" in result:
            result["http_headers"] = _header_map(
                result["http_headers"], f"route {key}.http_headers"
            )
        if "env_http_headers" in result:
            result["env_http_headers"] = _header_map(
                result["env_http_headers"],
                f"route {key}.env_http_headers",
                environment_values=True,
            )
        static_names = {name.lower() for name in result.get("http_headers", {})}
        environment_names = {name.lower() for name in result.get("env_http_headers", {})}
        overlap = sorted(static_names & environment_names)
        if overlap:
            raise ValueError(
                f"route {key} defines headers in both http_headers and env_http_headers: "
                + ", ".join(overlap)
            )
        result["base_url"] = _validate_http_base_url(result.get("base_url"), f"route {key}")
        result.setdefault("wire_api", "responses")
        if not isinstance(result["wire_api"], str) or result["wire_api"] != "responses":
            raise ValueError(f"route {key}: Codex custom routes require wire_api='responses'")
        for field, default, minimum, maximum in (
            ("request_max_retries", 1, 0, 2**63 - 1),
            ("stream_max_retries", 1, 0, 2**63 - 1),
            ("stream_idle_timeout_ms", 600_000, 1_000, 2**63 - 1),
        ):
            result.setdefault(field, default)
            result[field] = _positive_int(result[field], f"route {key}.{field}", minimum, maximum)
    elif driver == "codex_builtin":
        provider_id = result.get("provider_id")
        provider_id = _nonempty_string(provider_id, f"route {key}.provider_id")
        if provider_id not in {"openai", "amazon-bedrock"}:
            raise ValueError(
                f"route {key}: Codex built-in provider_id must be openai or amazon-bedrock"
            )
        result["provider_id"] = provider_id
        result.setdefault("auth", "chatgpt" if provider_id == "openai" else "builtin")
        expected_auth = "chatgpt" if provider_id == "openai" else "builtin"
        if result.get("auth") != expected_auth:
            raise ValueError(
                f"route {key}: auth must be {expected_auth!r} for built-in {provider_id!r}"
            )
        if result.get("auth_link_mode") is not None and not isinstance(
            result.get("auth_link_mode"), str
        ):
            raise ValueError(f"route {key}: invalid auth_link_mode")
        if result.get("auth_link_mode") not in {None, "shared", "copy", "none"}:
            raise ValueError(f"route {key}: invalid auth_link_mode")
    elif driver == "codex_oss":
        provider_id = result.get("provider_id")
        if not isinstance(provider_id, str) or provider_id not in {"ollama", "lmstudio"}:
            raise ValueError(f"route {key}: codex_oss provider_id must be ollama or lmstudio")
    elif driver == "catalog_only":
        if "base_url" in result:
            result["base_url"] = _validate_http_base_url(result["base_url"], f"route {key}")
        for field in ("wire_api", "auth"):
            if field in result:
                result[field] = _nonempty_string(result[field], f"route {key}.{field}")
    header_fields = {"http_headers", "env_http_headers", "extra_headers"}
    allowed_header_fields = {
        "switchyard": {"extra_headers"},
        "codex_custom": {"http_headers", "env_http_headers"},
    }.get(driver, set())
    unsupported_headers = sorted((header_fields & set(result)) - allowed_header_fields)
    if unsupported_headers:
        detail = f"header fields are unsupported by driver {driver}"
        raise ValueError(f"route {key}: {detail}: {unsupported_headers}")
    return result


def validate_model_entry(
    key: str, model: Mapping[str, Any], routes: Mapping[str, Any]
) -> dict[str, Any]:
    validate_id(key, "model id")
    _reject_unknown_fields(model, MODEL_FIELDS, f"model {key}")
    route_key = model.get("route")
    if not isinstance(route_key, str) or route_key not in routes:
        raise ValueError(f"model {key}: unknown route {route_key!r}")
    if not key.startswith(f"{route_key}__"):
        raise ValueError(
            f"model {key}: catalog key must start with exact route namespace "
            f"{route_key!r} followed by '__'"
        )
    if not isinstance(model.get("upstream_id"), str) or not model["upstream_id"].strip():
        raise ValueError(f"model {key}: upstream_id is required")
    if not isinstance(model.get("maker"), str) or not model["maker"].strip():
        raise ValueError(f"model {key}: maker is required")
    result = dict(model)
    result.setdefault("display_name", key)
    result.setdefault("description", "")
    result.setdefault("kind", "chat")
    result["display_name"] = _nonempty_string(result["display_name"], f"model {key}.display_name")
    if not isinstance(result["description"], str):
        raise ValueError(f"model {key}.description must be a string")
    result["kind"] = _enum_string(result["kind"], ALLOWED_MODEL_KINDS, f"model {key}.kind")
    result.setdefault(
        "agent_compatible",
        result["kind"] in {"chat", "vision_chat"} and routes[route_key]["driver"] != "catalog_only",
    )
    if not isinstance(result["agent_compatible"], bool):
        raise ValueError(f"model {key}.agent_compatible must be boolean")
    result.setdefault("context_window", 131072 if result["agent_compatible"] else 0)
    result["context_window"] = _positive_int(
        result["context_window"],
        f"model {key}.context_window",
        1024 if result["agent_compatible"] else 0,
        20_000_000,
    )
    result.setdefault("reasoning_levels", ["none", "low", "medium", "high"])
    result["reasoning_levels"] = _string_list(
        result["reasoning_levels"], f"model {key}.reasoning_levels", allow_empty=False
    )
    unknown_reasoning = sorted(set(result["reasoning_levels"]) - ALLOWED_REASONING)
    if unknown_reasoning:
        raise ValueError(f"model {key}: invalid reasoning levels {unknown_reasoning}")
    result.setdefault("default_reasoning", result["reasoning_levels"][-1])
    if (
        not isinstance(result["default_reasoning"], str)
        or result["default_reasoning"] not in result["reasoning_levels"]
    ):
        raise ValueError(f"model {key}: default_reasoning is not supported")
    route = routes[route_key]
    result["route_policy"] = _validate_openrouter_policy(
        result.get("route_policy"), f"model {key}.route_policy"
    )
    if result["route_policy"] is not None and route["api_operator"] != "openrouter":
        raise ValueError(f"model {key}: route_policy requires an OpenRouter route")
    if "extra_body" in result:
        if route["driver"] != "switchyard":
            raise ValueError(f"model {key}: extra_body is supported only by Switchyard targets")
        if not isinstance(result["extra_body"], Mapping):
            raise ValueError(f"model {key}.extra_body must be a table")
        result["extra_body"] = _json_compatible_value(
            result["extra_body"], f"model {key}.extra_body"
        )
    result.setdefault("modalities", route.get("transport_modalities", ["text"]))
    result["modalities"] = _string_list(
        result["modalities"], f"model {key}.modalities", allow_empty=False
    )
    unknown_modalities = sorted(set(result["modalities"]) - ALLOWED_MODALITIES)
    if unknown_modalities:
        raise ValueError(f"model {key}: invalid modalities {unknown_modalities}")
    missing_input_transport = sorted(set(result["modalities"]) - set(route["transport_modalities"]))
    if missing_input_transport:
        raise ValueError(
            f"model {key}: route transport cannot carry input modalities {missing_input_transport}"
        )
    result.setdefault("output_modalities", ["text"])
    result["output_modalities"] = _string_list(
        result["output_modalities"], f"model {key}.output_modalities", allow_empty=False
    )
    unknown_output_modalities = sorted(set(result["output_modalities"]) - ALLOWED_MODALITIES)
    if unknown_output_modalities:
        raise ValueError(f"model {key}: invalid output modalities {unknown_output_modalities}")
    missing_output_transport = sorted(
        set(result["output_modalities"]) - set(route["transport_output_modalities"])
    )
    if missing_output_transport:
        raise ValueError(
            f"model {key}: route transport cannot carry output modalities "
            f"{missing_output_transport}"
        )
    result.setdefault(
        "supports_tool_images",
        "image" in result["modalities"]
        and route.get("preserves_tool_media", False)
        and "image" in route.get("tool_result_modalities", []),
    )
    result.setdefault(
        "supports_documents",
        "file" in result["modalities"] and route.get("supports_documents", False),
    )
    for boolean_field in ("supports_tool_images", "supports_documents"):
        if not isinstance(result[boolean_field], bool):
            raise ValueError(f"model {key}.{boolean_field} must be boolean")
    if result["supports_tool_images"] and "image" not in result["modalities"]:
        raise ValueError(f"model {key}: supports_tool_images requires image input")
    if result["supports_documents"] and "file" not in result["modalities"]:
        raise ValueError(f"model {key}: supports_documents requires file input")
    result.setdefault("tool_calling", route.get("tool_calling", True))
    # OpenAI Responses distinguishes schema-defined function tools from
    # free-form custom tools.  Some compatible endpoints implement the former
    # but reject the latter.  Keep that narrower capability independent so a
    # model can still use shell and MCP function tools without receiving
    # Codex's free-form apply_patch tool.
    result.setdefault("supports_custom_tools", result["tool_calling"])
    result.setdefault("parallel_tool_calls", route.get("parallel_tool_calls", True))
    result.setdefault("supports_reasoning_summaries", False)
    result.setdefault("structured_output", False)
    for boolean_field in (
        "tool_calling",
        "supports_custom_tools",
        "parallel_tool_calls",
        "supports_reasoning_summaries",
        "structured_output",
    ):
        if not isinstance(result[boolean_field], bool):
            raise ValueError(f"model {key}.{boolean_field} must be boolean")
    if result["tool_calling"] and not route["tool_calling"]:
        raise ValueError(f"model {key}: tool_calling exceeds route transport capability")
    if result["supports_custom_tools"] and not result["tool_calling"]:
        raise ValueError(f"model {key}: supports_custom_tools requires tool_calling")
    if result["parallel_tool_calls"] and not result["tool_calling"]:
        raise ValueError(f"model {key}: parallel_tool_calls requires tool_calling")
    if result["parallel_tool_calls"] and not route["parallel_tool_calls"]:
        raise ValueError(f"model {key}: parallel_tool_calls exceeds route transport capability")
    result.setdefault("max_output_tokens", None)
    if result["max_output_tokens"] is not None:
        result["max_output_tokens"] = _positive_int(
            result["max_output_tokens"], f"model {key}.max_output_tokens", 1, 20_000_000
        )
    result.setdefault("availability", "current")
    result.setdefault("capability_confidence", "documented")
    result.setdefault("source", "bundled-catalog")
    for field in ("availability", "capability_confidence", "source"):
        result[field] = _nonempty_string(result[field], f"model {key}.{field}")
    for field in (
        "availability_source",
        "capability_source",
        "pricing_source",
        "inventory",
        "resource_group",
    ):
        if field in result:
            result[field] = _optional_nonempty_string(result[field], f"model {key}.{field}")
    result.setdefault("resource_group", route.get("resource_group"))
    for field in (
        "input_cost_per_million",
        "cached_input_cost_per_million",
        "cache_write_input_cost_per_million",
        "output_cost_per_million",
        "unit_cost_usd",
    ):
        if field not in result:
            continue
        value = result[field]
        if (
            not isinstance(value, (int, float))
            or isinstance(value, bool)
            or (isinstance(value, float) and not math.isfinite(value))
            or value < 0
        ):
            raise ValueError(f"model {key}.{field} must be a non-negative finite number")
    return result


def _validate_models(
    values: Mapping[str, Any], routes: Mapping[str, Any]
) -> dict[str, dict[str, Any]]:
    """Validate model rows and reject duplicate route/upstream identities."""

    result: dict[str, dict[str, Any]] = {}
    identities: dict[tuple[str, str], str] = {}
    for key, value in values.items():
        model = validate_model_entry(key, value, routes)
        identity = (str(model["route"]), str(model["upstream_id"]))
        previous = identities.get(identity)
        if previous is not None:
            raise ValueError(
                f"models {previous!r} and {key!r} duplicate route/upstream binding {identity!r}"
            )
        identities[identity] = key
        result[key] = model
    return result


def _validate_resource(key: str, resource: Mapping[str, Any]) -> dict[str, Any]:
    validate_id(key, "resource group id")
    _reject_unknown_fields(resource, RESOURCE_FIELDS, f"resource {key}")
    result = dict(resource)
    result.setdefault("description", "")
    if not isinstance(result["description"], str):
        raise ValueError(f"resource {key}.description must be a string")
    result.setdefault("lock_key", key)
    if not isinstance(result["lock_key"], str) or not result["lock_key"]:
        raise ValueError(f"resource {key}: lock_key is required")
    result.setdefault("max_active", 4)
    result["max_active"] = _positive_int(
        result["max_active"], f"resource {key}.max_active", 1, 1024
    )
    return result


def _validate_resources(values: Mapping[str, Any]) -> dict[str, dict[str, Any]]:
    result = {key: _validate_resource(key, value) for key, value in values.items()}
    capacities: dict[str, tuple[str, int]] = {}
    for key, resource in result.items():
        lock_key = str(resource["lock_key"])
        maximum = int(resource["max_active"])
        previous = capacities.get(lock_key)
        if previous is not None and previous[1] != maximum:
            raise ValueError(
                f"resources {previous[0]!r} and {key!r} share lock_key {lock_key!r} "
                f"with conflicting max_active values {previous[1]} and {maximum}"
            )
        capacities[lock_key] = (key, maximum)
    return result
