"""{{ display_name }} MCP server — generated by mcpify. Do not hand-edit.

`jsonschema`-based input/output validation. Schemas themselves are NOT
embedded in this file — they're loaded from a per-version co-located
`generated_schemas(_v<label>).json.zst` asset (written directly by the Rust
generator, not templated, keeping this file's size independent of how many
operations the source spec declares), keyed by the active `api_version`.

The asset is zstd-compressed: every operation's schema embeds a full copy
of the spec's `$defs` library (a deliberate generator tradeoff — see
`mcpify`'s `openapi::schema_resolve` — simpler than cross-schema `$ref`
resolution, at the cost of file size), and a long-distance-matching
compressor collapses that duplication (measured ~190 MB down to tens of KB
on a real spec) without requiring any change to the JSON Schema shape
itself. Decompressed once per version, lazily, into the cache below.
"""

from __future__ import annotations

import json
from functools import lru_cache
from pathlib import Path
from typing import Any

import jsonschema
import zstandard

from ..core.errors import ValidationError

_SCHEMAS_DIR = Path(__file__).parent

# mcpify:versions:begin
_VERSION_SCHEMAS_FILES: dict[str, str] = {
{%- for v in version_entries %}
    "{{ v.label }}": "{{ v.schemas_file }}",
{%- endfor %}
}
# mcpify:versions:end


@lru_cache(maxsize=None)
def _schemas_by_operation(api_version: str) -> dict[str, dict[str, Any]]:
    file_name = _VERSION_SCHEMAS_FILES.get(api_version)
    if file_name is None:
        return {}
    try:
        compressed = (_SCHEMAS_DIR / file_name).read_bytes()
        return json.loads(zstandard.ZstdDecompressor().decompress(compressed))
    except (OSError, zstandard.ZstdError, json.JSONDecodeError):
        return {}


@lru_cache(maxsize=None)
def _validator_for(api_version: str, operation_id: str, schema_key: str) -> jsonschema.protocols.Validator:
    schema = _schemas_by_operation(api_version).get(operation_id, {}).get(schema_key, {})
    validator_cls = jsonschema.validators.validator_for(schema)
    return validator_cls(schema)


def _validate_against(api_version: str, operation_id: str, schema_key: str, data: Any) -> list[str]:
    validator = _validator_for(api_version, operation_id, schema_key)
    return [error.message for error in validator.iter_errors(data)]


def validate_input(api_version: str, operation_id: str, data: Any) -> None:
    """Raises `ValidationError` if `data` doesn't satisfy `operation_id`'s
    input schema, for the given `api_version`.
    """
    errors = _validate_against(api_version, operation_id, "inputSchema", data)
    if errors:
        raise ValidationError(f"invalid input for '{operation_id}'", details=errors)


def validate_output(api_version: str, operation_id: str, data: Any) -> None:
    """Raises `ValidationError` if `data` doesn't satisfy `operation_id`'s
    output schema — surfaces upstream API drift as a structured error
    rather than silently returning a mismatched response (architecture.md's
    `call` pipeline). Validates against the given `api_version`'s schema.
    """
    errors = _validate_against(api_version, operation_id, "outputSchema", data)
    if errors:
        raise ValidationError(f"unexpected response shape for '{operation_id}'", details=errors)
