"""Email sending status tracking."""

from typing import Any
from typing import Literal

from pydantic import BaseModel
from pydantic import ConfigDict
from pydantic import Field

# Send status constants (normalized across all ESP backends)
SendStatusType = Literal[
    'sent',      # ESP has sent the message (may or may not be delivered yet)
    'queued',    # ESP will try to send the message later
    'invalid',   # Recipient email address is not valid
    'rejected',  # Recipient is blacklisted or rejected by ESP
    'failed',    # Send attempt failed for some other reason
    'unknown',   # Status could not be determined
]


class RecipientStatus(BaseModel):
    """
    Status information for a single recipient.

    Tracks the ESP message ID and send status for an individual recipient.
    """

    message_id: str | None = Field(
        default=None,
        description='ESP-assigned message ID for this recipient (None if send failed)',
    )
    status: SendStatusType | None = Field(
        default=None,
        description='Send status for this recipient (None if not yet sent to ESP)',
    )


class SendStatus(BaseModel):
    """
    Complete status information for sent email message(s).

    Contains aggregated status across all recipients, per-recipient details,
    and raw ESP response data.

    Attributes:
        message_id: ESP message ID(s). Single string if all recipients share same ID,
                   set of strings if different IDs, or None if send failed.
        status: Set of send statuses across all recipients.
        recipients: Per-recipient status information, keyed by email address.
        esp_response: Raw response from ESP API (for debugging/ESP-specific features).
    """

    message_id: str | set[str] | None = Field(
        default=None,
        description='ESP message ID(s) - single ID, set of IDs, or None',
    )
    status: set[SendStatusType] | None = Field(
        default=None,
        description='Set of send statuses across all recipients',
    )
    recipients: dict[str, RecipientStatus] = Field(
        default_factory=dict,
        description='Per-recipient status information (email -> RecipientStatus)',
    )
    esp_response: Any = Field(
        default=None,
        description='Raw ESP API response (non-portable, for debugging)',
    )

    model_config = ConfigDict(arbitrary_types_allowed=True)

    def set_recipient_status(self, recipients: dict[str, RecipientStatus]) -> None:
        """
        Update recipient statuses and derive aggregated message_id and status.

        This method updates the recipients dictionary and automatically computes
        the aggregated message_id and status fields based on all recipients.

        Args:
            recipients: Dictionary of email -> RecipientStatus to add/update

        Example:
            >>> status = SendStatus()
            >>> status.set_recipient_status({
            ...     'user@example.com': RecipientStatus(
            ...         message_id='msg-123',
            ...         status='sent'
            ...     )
            ... })
            >>> status.message_id
            'msg-123'
            >>> status.status
            {'sent'}
        """
        # Update recipients dict
        self.recipients.update(recipients)

        # Collect unique message IDs
        message_ids = {r.message_id for r in self.recipients.values() if r.message_id}

        if len(message_ids) == 0:
            self.message_id = None
        elif len(message_ids) == 1:
            # Single message ID - simplify to string
            self.message_id = message_ids.pop()
        else:
            # Multiple message IDs - keep as set
            self.message_id = message_ids

        # Collect unique statuses
        statuses = {r.status for r in self.recipients.values() if r.status}
        self.status = statuses if statuses else None

    @property
    def is_success(self) -> bool:
        """
        Check if all recipients were successfully sent or queued.

        Returns:
            True if all recipients have status 'sent' or 'queued', False otherwise.

        Example:
            >>> status = SendStatus(status={'sent', 'queued'})
            >>> status.is_success
            True
            >>> status = SendStatus(status={'sent', 'failed'})
            >>> status.is_success
            False
        """
        if not self.status:
            return False
        return self.status.issubset({'sent', 'queued'})

    @property
    def has_failures(self) -> bool:
        """
        Check if any recipients failed to send.

        Returns:
            True if any recipient has status 'failed', 'rejected', or 'invalid'.

        Example:
            >>> status = SendStatus(status={'sent', 'failed'})
            >>> status.has_failures
            True
        """
        if not self.status:
            return False
        return bool(self.status.intersection({'failed', 'rejected', 'invalid'}))

    def get_failed_recipients(self) -> list[str]:
        """
        Get list of email addresses that failed to send.

        Returns:
            List of email addresses with failed/rejected/invalid status.

        Example:
            >>> status = SendStatus(recipients={
            ...     'good@example.com': RecipientStatus(status='sent'),
            ...     'bad@example.com': RecipientStatus(status='failed'),
            ... })
            >>> status.get_failed_recipients()
            ['bad@example.com']
        """
        return [
            email
            for email, recipient in self.recipients.items()
            if recipient.status in {'failed', 'rejected', 'invalid'}
        ]

    def get_successful_recipients(self) -> list[str]:
        """
        Get list of email addresses that were successfully sent or queued.

        Returns:
            List of email addresses with sent/queued status.

        Example:
            >>> status = SendStatus(recipients={
            ...     'good@example.com': RecipientStatus(status='sent'),
            ...     'bad@example.com': RecipientStatus(status='failed'),
            ... })
            >>> status.get_successful_recipients()
            ['good@example.com']
        """
        return [
            email for email, recipient in self.recipients.items() if recipient.status in {'sent', 'queued'}
        ]
