"""Typed dispatch envelopes."""

from collections.abc import Mapping
from dataclasses import dataclass
from typing import Any, cast
from uuid import uuid4

from aws_lambda_typing.events import APIGatewayProxyEventV1, EventBridgeEvent


@dataclass(slots=True, frozen=True)
class ApiRequest:
    raw_event: APIGatewayProxyEventV1
    method: str
    resource: str
    path: str
    headers: dict[str, str]
    path_parameters: dict[str, str]
    query_string_parameters: dict[str, str]
    correlation_id: str

    @classmethod
    def from_event(cls, event: APIGatewayProxyEventV1) -> "ApiRequest":
        normalized = _normalize_api_event(event)
        headers = {str(key).lower(): str(value) for key, value in (normalized.get("headers") or {}).items()}
        resource = str(normalized.get("resource") or "")
        path = str(normalized.get("path") or normalized.get("rawPath") or resource)
        path_parameters = {
            str(key): str(value) for key, value in (normalized.get("pathParameters") or {}).items() if value is not None
        }
        query_string_parameters = {
            str(key): str(value)
            for key, value in (normalized.get("queryStringParameters") or {}).items()
            if value is not None
        }
        return cls(
            raw_event=normalized,
            method=str(normalized.get("httpMethod") or "").upper(),
            resource=resource,
            path=path,
            headers=headers,
            path_parameters=path_parameters,
            query_string_parameters=query_string_parameters,
            correlation_id=headers.get("x-correlation-id") or str(uuid4()),
        )


@dataclass(slots=True, frozen=True)
class EventEnvelope:
    raw_event: EventBridgeEvent
    detail_type: str
    source: str
    detail: Mapping[str, Any]
    correlation_id: str

    @classmethod
    def from_event(cls, event: EventBridgeEvent) -> "EventEnvelope":
        detail = event.get("detail")
        payload = cast(Mapping[str, Any], detail if isinstance(detail, Mapping) else {})
        correlation = payload.get("correlation_id")
        return cls(
            raw_event=event,
            detail_type=str(event.get("detail-type") or ""),
            source=str(event.get("source") or ""),
            detail=payload,
            correlation_id=str(correlation) if correlation else str(uuid4()),
        )


def _normalize_api_event(event: APIGatewayProxyEventV1) -> APIGatewayProxyEventV1:
    if event.get("resource"):
        return event

    resource_path = (event.get("requestContext") or {}).get("resourcePath")
    if isinstance(resource_path, str) and resource_path:
        normalized = dict(event)
        normalized["resource"] = resource_path
        return cast(APIGatewayProxyEventV1, normalized)

    path = event.get("path") or event.get("rawPath")
    if not isinstance(path, str) or not path:
        return event

    resource = path
    path_params = event.get("pathParameters")
    if isinstance(path_params, dict):
        for param_name, param_value in path_params.items():
            if not isinstance(param_name, str) or not isinstance(param_value, str):
                continue
            resource = resource.replace(f"/{param_value}", f"/{{{param_name}}}", 1)

    normalized = dict(event)
    normalized["resource"] = resource
    return cast(APIGatewayProxyEventV1, normalized)


__all__ = ["ApiRequest", "EventEnvelope"]
