"""Convert OpenAPI webhooks to the LAPIS [webhooks] section."""

from __future__ import annotations

from typing import Any

from lapis_spec.models.lapis import LapisField, LapisWebhook
from lapis_spec.models.openapi import OpenAPIDict
from lapis_spec.type_mapper import map_type
from lapis_spec.utils import to_snake_case, truncate_description


def convert_webhooks(
    doc: OpenAPIDict, schema_name_map: dict[str, str]
) -> list[LapisWebhook]:
    """Build LAPIS webhook definitions from OpenAPI webhooks.

    Supports OpenAPI 3.1 native ``webhooks`` and the ``x-webhooks``
    extension for OpenAPI 3.0.
    """
    webhooks_data = doc.get("webhooks") or doc.get("x-webhooks") or {}
    if not isinstance(webhooks_data, dict):
        return []

    result: list[LapisWebhook] = []
    for name, webhook_item in webhooks_data.items():
        if not isinstance(webhook_item, dict):
            continue
        wh = _convert_webhook(name, webhook_item, schema_name_map)
        if wh:
            result.append(wh)
    return result


def _convert_webhook(
    name: str, item: dict[str, Any], schema_name_map: dict[str, str]
) -> LapisWebhook | None:
    for method in ("post", "put", "patch", "get", "delete"):
        op = item.get(method)
        if not isinstance(op, dict):
            continue

        wh_name = op.get("operationId") or to_snake_case(name)
        desc = truncate_description(op.get("summary") or op.get("description"))

        triggers: list[str] = []
        for trigger in op.get("x-triggers", []):
            if isinstance(trigger, str):
                triggers.append(trigger)

        outputs = _extract_webhook_outputs(op, schema_name_map)

        path = f"/webhooks/{name.replace('_', '-')}"

        return LapisWebhook(
            name=wh_name,
            method=method.upper(),
            path=path,
            description=desc,
            triggers=triggers,
            outputs=outputs,
        )
    return None


def _extract_webhook_outputs(
    op: dict[str, Any], schema_name_map: dict[str, str]
) -> list[LapisField]:
    body = op.get("requestBody", {})
    if not isinstance(body, dict):
        return []
    content = body.get("content", {})
    json_content = content.get("application/json", {})
    schema = json_content.get("schema", {})
    if not isinstance(schema, dict):
        return []

    props = schema.get("properties", {})
    fields: list[LapisField] = []

    headers: dict[str, str] = {}
    for hdr_name, hdr_mapping in op.get("x-webhook-headers", {}).items():
        if isinstance(hdr_mapping, str):
            headers[hdr_mapping] = hdr_name

    for field_name, field_schema in props.items():
        if not isinstance(field_schema, dict):
            continue
        type_expr = _resolve_webhook_type(field_schema, schema_name_map)
        required = field_name in set(schema.get("required", []))
        header_name = headers.get(field_name)
        fields.append(
            LapisField(
                name=field_name,
                type_expr=type_expr,
                optional=not required,
                header_name=header_name,
            )
        )

    return fields


def _resolve_webhook_type(schema: dict[str, Any], schema_name_map: dict[str, str]) -> str:
    stored = schema.get("x-schema-name")
    if stored and isinstance(stored, str) and stored in schema_name_map:
        return schema_name_map[stored]

    if "enum" in schema:
        for name, lapis_name in schema_name_map.items():
            if schema.get("x-schema-name") == name:
                return lapis_name

    return map_type(schema)
