"""Dispatch contracts."""

from dataclasses import dataclass
from typing import ParamSpec, Protocol, TypeVar, runtime_checkable

from aws_lambda_typing.events import APIGatewayProxyEventV1
from aws_lambda_typing.responses import APIGatewayProxyResponseV1

from ..support.authz import Principal
from .models import ApiRequest, EventEnvelope

ResponseT = TypeVar("ResponseT", covariant=True)
UseCaseP = ParamSpec("UseCaseP")


@dataclass(slots=True, frozen=True)
class ApiRoute:
    method: str
    resource: str

    def __post_init__(self) -> None:
        object.__setattr__(self, "method", self.method.upper())


@dataclass(slots=True, frozen=True)
class EventPolicy:
    detail_type: str
    source: str | None = None


@runtime_checkable
class IRestHandler(Protocol[ResponseT]):
    route: ApiRoute

    def handle(self, request: ApiRequest) -> ResponseT: ...


@runtime_checkable
class IPolicyHandler(Protocol[ResponseT]):
    policy: EventPolicy

    def handle(self, event: EventEnvelope) -> ResponseT: ...


@runtime_checkable
class RequestAuthenticator(Protocol):
    def authenticate(self, request: ApiRequest) -> Principal: ...


@runtime_checkable
class IJwtTokenHandler(RequestAuthenticator, Protocol):
    def __call__(self, event: APIGatewayProxyEventV1 | ApiRequest) -> None: ...


@runtime_checkable
class ICorsHandler(Protocol):
    def preflight(self, request: ApiRequest) -> APIGatewayProxyResponseV1 | None: ...

    def apply(self, request: ApiRequest, response: APIGatewayProxyResponseV1) -> APIGatewayProxyResponseV1: ...


@runtime_checkable
class ApiRouteResolver(Protocol):
    def resolve(self, request: ApiRequest) -> IRestHandler[APIGatewayProxyResponseV1]: ...


@runtime_checkable
class PolicyResolver(Protocol):
    def resolve(self, event: EventEnvelope) -> IPolicyHandler[ResponseT]: ...


@runtime_checkable
class ApiExceptionMapper(Protocol):
    def map_exception(self, exc: Exception) -> APIGatewayProxyResponseV1: ...


@runtime_checkable
class PrincipalProvider(Protocol):
    def principal_for(self, event: EventEnvelope) -> Principal: ...


@runtime_checkable
class IUseCase(Protocol[UseCaseP, ResponseT]):
    def __call__(self, *args: UseCaseP.args, **kwargs: UseCaseP.kwargs) -> ResponseT: ...


__all__ = [
    "ApiExceptionMapper",
    "ApiRoute",
    "ApiRouteResolver",
    "EventPolicy",
    "ICorsHandler",
    "IJwtTokenHandler",
    "IPolicyHandler",
    "IRestHandler",
    "IUseCase",
    "PolicyResolver",
    "PrincipalProvider",
    "RequestAuthenticator",
]
