"""
Normalize an OpenAPI schema dict so backend-specific framing doesn't make
two semantically-equivalent schemas compare unequal.

Drops:
- Top-level OpenAPI envelope fields that vary trivially (info, servers, tags)
- Django-only x-mizan-functions sub-fields (form metadata)
- Django-only top-level extensions (x-mizan-channels)

Sorts:
- x-mizan-functions by name
- components.schemas keys
"""

from __future__ import annotations

from copy import deepcopy
from typing import Any


_DJANGO_ONLY_FUNCTION_FIELDS = {"isForm", "formName", "formRole", "formFields", "formFieldsError"}

_OPENAPI_ENVELOPE_FIELDS_TO_DROP = {"info", "servers", "tags", "openapi"}


def normalize(schema: dict[str, Any]) -> dict[str, Any]:
    """Return a canonicalized copy suitable for deep-equal comparison."""
    out = deepcopy(schema)

    for k in _OPENAPI_ENVELOPE_FIELDS_TO_DROP:
        out.pop(k, None)

    out.pop("x-mizan-channels", None)

    fns = out.get("x-mizan-functions") or []
    for fn in fns:
        for field in _DJANGO_ONLY_FUNCTION_FIELDS:
            fn.pop(field, None)
    out["x-mizan-functions"] = sorted(fns, key=lambda f: f["name"])

    components = out.get("components", {})
    schemas = components.get("schemas")
    if isinstance(schemas, dict):
        components["schemas"] = {k: schemas[k] for k in sorted(schemas)}

    return out


def afi_subset(schema: dict[str, Any]) -> dict[str, Any]:
    """Just the AFI-essential surface — what every adapter must agree on."""
    fns = schema.get("x-mizan-functions") or []
    fns_clean = [
        {k: v for k, v in fn.items() if k not in _DJANGO_ONLY_FUNCTION_FIELDS}
        for fn in fns
    ]
    return {
        "x-mizan-functions": sorted(fns_clean, key=lambda f: f["name"]),
        "x-mizan-contexts": schema.get("x-mizan-contexts", {}),
    }


def function_io_schemas(schema: dict[str, Any]) -> dict[str, Any]:
    """
    Subset of components.schemas containing only the per-function Input/Output
    models (what the codegen actually consumes for TypeScript type generation).
    Drops backend-specific noise like HTTPValidationError, ValidationError,
    that one backend emits and the other doesn't.
    """
    fns = schema.get("x-mizan-functions") or []
    expected_names: set[str] = set()
    for fn in fns:
        if fn.get("inputType"):
            expected_names.add(fn["inputType"])
        if fn.get("outputType"):
            expected_names.add(fn["outputType"])

    components = schema.get("components", {})
    schemas = components.get("schemas", {})
    return {name: schemas[name] for name in sorted(expected_names) if name in schemas}
