"""EventBridge dispatch."""

from dataclasses import dataclass
from typing import Any

from aws_lambda_typing.events import EventBridgeEvent

from ..runtime.context import RequestScope, current_container
from ..support.authz import Principal
from ..support.errors import AmbiguousPolicyError, PolicyNotFoundError
from .contracts import EventPolicy, IPolicyHandler, PolicyResolver, PrincipalProvider
from .models import EventEnvelope


@dataclass(slots=True, frozen=True)
class DefaultPolicyResolver(PolicyResolver):
    """Resolve one policy handler for an event."""

    handlers: tuple[IPolicyHandler[Any], ...] = ()

    def __init__(self, handlers: list[IPolicyHandler[Any]] | None = None) -> None:
        object.__setattr__(self, "handlers", tuple(handlers or ()))

    def resolve(self, event: EventEnvelope) -> IPolicyHandler[Any]:
        matches = [handler for handler in self.handlers if _policy_matches(handler.policy, event)]
        if not matches:
            raise PolicyNotFoundError(f"No policy handler matched {event.source}:{event.detail_type}")
        if len(matches) > 1:
            names = ", ".join(type(handler).__name__ for handler in matches)
            raise AmbiguousPolicyError(f"Multiple policy handlers matched {event.source}:{event.detail_type}: {names}")
        return matches[0]


def _policy_matches(policy: EventPolicy, event: EventEnvelope) -> bool:
    source_matches = policy.source in {None, "*", event.source}
    return source_matches and policy.detail_type == event.detail_type


@dataclass(slots=True, frozen=True)
class SystemPrincipalProvider(PrincipalProvider):
    """Provide the system principal."""

    def principal_for(self, event: EventEnvelope) -> Principal:
        return Principal.system()


@dataclass(slots=True, frozen=True)
class EventBridgeEventHandler:
    """Dispatch an EventBridge event."""

    resolver: PolicyResolver
    principal_provider: PrincipalProvider

    def handle(self, event: EventEnvelope | EventBridgeEvent) -> Any:
        envelope = event if isinstance(event, EventEnvelope) else EventEnvelope.from_event(event)
        principal = self.principal_provider.principal_for(envelope)
        with RequestScope(
            correlation_id=envelope.correlation_id,
            principal=principal,
            container=current_container(),
        ):
            handler: IPolicyHandler[Any] = self.resolver.resolve(envelope)
            return handler.handle(envelope)


__all__ = ["DefaultPolicyResolver", "EventBridgeEventHandler", "SystemPrincipalProvider"]
