from __future__ import annotations

from collections.abc import Mapping
from dataclasses import dataclass, field
from datetime import datetime
from typing import Protocol, runtime_checkable


CAPABILITY_MAIL_DELIVERY_OUTBOX = "mail.delivery_outbox"
CAPABILITY_MAIL_NOTIFICATION_DELIVERY = "mail.notificationDelivery"
CAPABILITY_MAIL_BOUNCE_PROCESSING = "mail.bounce_processing"


@dataclass(frozen=True, slots=True)
class NotificationMailDeliveryRequest:
    tenant_id: str
    notification_id: str
    recipient: str
    subject: str
    body_text: str
    body_html: str | None = None
    action_url: str | None = None
    mail_profile_id: str | None = None
    from_address: str | None = None
    smtp_server_id: str | None = None
    smtp_credential_id: str | None = None
    metadata: Mapping[str, object] = field(default_factory=dict)


@runtime_checkable
class NotificationMailDeliveryProvider(Protocol):
    """Mail-owned durable submission boundary for notification email."""

    def submit_notification_mail(
        self,
        session: object,
        request: NotificationMailDeliveryRequest,
    ) -> Mapping[str, object]:
        ...

@runtime_checkable
class MailDeliveryOutboxProvider(Protocol):
    """Stable worker boundary for Mail-owned external delivery effects."""

    def dispatch_due(
        self,
        session: object,
        *,
        tenant_id: str | None = None,
        limit: int = 25,
        worker_id: str | None = None,
    ) -> Mapping[str, object]:
        ...

    def purge_expired(
        self,
        session: object,
        *,
        limit: int = 250,
    ) -> Mapping[str, object]:
        ...


@dataclass(frozen=True, slots=True)
class MailBounceObservationRef:
    id: str
    tenant_id: str
    profile_id: str
    folder: str
    uid: str
    original_message_id: str | None
    command_id: str | None
    recipient: str | None
    action: str
    status_code: str | None
    diagnostic: str | None
    permanent: bool
    observed_at: datetime
    matched: bool
    evidence: Mapping[str, object] = field(default_factory=dict)


@runtime_checkable
class MailBounceProcessingProvider(Protocol):
    """Mail-owned DSN ingestion and durable correlation boundary."""

    def process_raw_message(
        self,
        session: object,
        *,
        tenant_id: str,
        profile_id: str,
        folder: str,
        uid: str,
        raw_message: bytes,
    ) -> tuple[MailBounceObservationRef, ...]:
        ...

    def scan_due(
        self,
        session: object,
        *,
        tenant_id: str | None = None,
        limit: int = 100,
    ) -> Mapping[str, object]:
        ...

    def observations_for_commands(
        self,
        session: object,
        *,
        tenant_id: str,
        command_ids: tuple[str, ...],
    ) -> Mapping[str, tuple[MailBounceObservationRef, ...]]:
        ...


def notification_mail_delivery_provider(
    registry: object | None,
) -> NotificationMailDeliveryProvider | None:
    if (
        registry is None
        or not hasattr(registry, "has_capability")
        or not registry.has_capability(CAPABILITY_MAIL_NOTIFICATION_DELIVERY)
    ):
        return None
    provider = registry.require_capability(CAPABILITY_MAIL_NOTIFICATION_DELIVERY)
    if not isinstance(provider, NotificationMailDeliveryProvider):
        raise TypeError(
            "mail.notificationDelivery provider does not implement "
            "NotificationMailDeliveryProvider"
        )
    return provider


def mail_bounce_processing_provider(
    registry: object | None,
) -> MailBounceProcessingProvider | None:
    if (
        registry is None
        or not hasattr(registry, "has_capability")
        or not registry.has_capability(CAPABILITY_MAIL_BOUNCE_PROCESSING)
    ):
        return None
    provider = registry.require_capability(CAPABILITY_MAIL_BOUNCE_PROCESSING)
    if not isinstance(provider, MailBounceProcessingProvider):
        raise TypeError(
            "mail.bounce_processing provider does not implement "
            "MailBounceProcessingProvider"
        )
    return provider
