"""TypedDict models for the UseSend API.

Lightweight, Pydantic-free types for editor autocomplete and static checks.
At runtime these are plain dicts and lists.
"""

from __future__ import annotations

from datetime import datetime
from typing import Any, Dict, List, Optional, Union, TypedDict
from typing_extensions import NotRequired, Required, Literal

# ---------------------------------------------------------------------------
# Domains
# ---------------------------------------------------------------------------

DomainStatus = Literal[
    "NOT_STARTED",
    "PENDING",
    "SUCCESS",
    "FAILED",
    "TEMPORARY_FAILURE",
]

DNSRecordType = Literal["MX", "TXT"]


class DNSRecord(TypedDict, total=False):
    type: DNSRecordType
    name: str
    value: str
    ttl: str
    priority: Optional[str]
    status: DomainStatus
    recommended: Optional[bool]


DNSRecords = List[DNSRecord]


class Domain(TypedDict, total=False):
    id: float
    name: str
    teamId: float
    status: DomainStatus
    region: str
    clickTracking: bool
    openTracking: bool
    publicKey: str
    dkimStatus: Optional[str]
    spfDetails: Optional[str]
    createdAt: str
    updatedAt: str
    dmarcAdded: bool
    isVerifying: bool
    errorMessage: Optional[str]
    subdomain: Optional[str]
    verificationError: Optional[str]
    lastCheckedTime: Optional[str]
    dnsRecords: DNSRecords


DomainList = List[Domain]


class DomainCreate(TypedDict):
    name: str
    region: str


class DomainCreateResponse(TypedDict, total=False):
    id: float
    name: str
    teamId: float
    status: DomainStatus
    region: str
    clickTracking: bool
    openTracking: bool
    publicKey: str
    dkimStatus: Optional[str]
    spfDetails: Optional[str]
    createdAt: str
    updatedAt: str
    dmarcAdded: bool
    isVerifying: bool
    errorMessage: Optional[str]
    subdomain: Optional[str]
    verificationError: Optional[str]
    lastCheckedTime: Optional[str]
    dnsRecords: DNSRecords


class DomainVerifyResponse(TypedDict):
    message: str


class DomainDeleteResponse(TypedDict):
    id: int
    success: bool
    message: str


# ---------------------------------------------------------------------------
# Emails
# ---------------------------------------------------------------------------

EmailEventStatus = Literal[
    "SCHEDULED",
    "QUEUED",
    "SENT",
    "DELIVERY_DELAYED",
    "BOUNCED",
    "REJECTED",
    "RENDERING_FAILURE",
    "DELIVERED",
    "OPENED",
    "CLICKED",
    "COMPLAINED",
    "FAILED",
    "CANCELLED",
]


class EmailEvent(TypedDict, total=False):
    emailId: str
    status: EmailEventStatus
    createdAt: str
    data: Optional[Any]


Email = TypedDict(
    "Email",
    {
        "id": str,
        "teamId": float,
        "to": Union[str, List[str]],
        "replyTo": NotRequired[Union[str, List[str]]],
        "cc": NotRequired[Union[str, List[str]]],
        "bcc": NotRequired[Union[str, List[str]]],
        "from": str,
        "subject": str,
        "html": str,
        "text": str,
        "createdAt": str,
        "updatedAt": str,
        "emailEvents": List[EmailEvent],
    },
)


class EmailUpdate(TypedDict):
    # Accept datetime or ISO string; client will JSON-encode
    scheduledAt: Union[datetime, str]


class EmailUpdateResponse(TypedDict, total=False):
    emailId: Optional[str]


EmailLatestStatus = Literal[
    "SCHEDULED",
    "QUEUED",
    "SENT",
    "DELIVERY_DELAYED",
    "BOUNCED",
    "REJECTED",
    "RENDERING_FAILURE",
    "DELIVERED",
    "OPENED",
    "CLICKED",
    "COMPLAINED",
    "FAILED",
    "CANCELLED",
]


EmailListItem = TypedDict(
    "EmailListItem",
    {
        "id": str,
        "to": Union[str, List[str]],
        "replyTo": NotRequired[Union[str, List[str]]],
        "cc": NotRequired[Union[str, List[str]]],
        "bcc": NotRequired[Union[str, List[str]]],
        "from": str,
        "subject": str,
        "html": str,
        "text": str,
        "createdAt": str,
        "updatedAt": str,
        "latestStatus": EmailLatestStatus,
        "scheduledAt": str,
        "domainId": float,
    },
)


class EmailsList(TypedDict):
    data: List[EmailListItem]
    count: float


class Attachment(TypedDict):
    filename: str
    content: str


EmailCreate = TypedDict(
    "EmailCreate",
    {
        "to": Required[Union[str, List[str]]],
        "from": Required[str],
        "subject": NotRequired[str],
        "templateId": NotRequired[str],
        "variables": NotRequired[Dict[str, str]],
        "replyTo": NotRequired[Union[str, List[str]]],
        "cc": NotRequired[Union[str, List[str]]],
        "bcc": NotRequired[Union[str, List[str]]],
        "text": NotRequired[str],
        "html": NotRequired[str],
        "attachments": NotRequired[List[Attachment]],
        "scheduledAt": NotRequired[Union[datetime, str]],
        "inReplyToId": NotRequired[str],
        "headers": NotRequired[Dict[str, str]],
    },
)


class EmailCreateResponse(TypedDict, total=False):
    emailId: Optional[str]


EmailBatchItem = TypedDict(
    "EmailBatchItem",
    {
        "to": Required[Union[str, List[str]]],
        "from": Required[str],
        "subject": NotRequired[str],
        "templateId": NotRequired[str],
        "variables": NotRequired[Dict[str, str]],
        "replyTo": NotRequired[Union[str, List[str]]],
        "cc": NotRequired[Union[str, List[str]]],
        "bcc": NotRequired[Union[str, List[str]]],
        "text": NotRequired[str],
        "html": NotRequired[str],
        "attachments": NotRequired[List[Attachment]],
        "scheduledAt": NotRequired[Union[datetime, str]],
        "inReplyToId": NotRequired[str],
        "headers": NotRequired[Dict[str, str]],
    },
)


EmailBatch = List[EmailBatchItem]


class EmailBatchResponseItem(TypedDict):
    emailId: str


class EmailBatchResponse(TypedDict):
    data: List[EmailBatchResponseItem]


class EmailCancelResponse(TypedDict, total=False):
    emailId: Optional[str]


# ---------------------------------------------------------------------------
# Contacts
# ---------------------------------------------------------------------------


class ContactBookCounts(TypedDict, total=False):
    contacts: int


class ContactBook(TypedDict, total=False):
    id: str
    name: str
    teamId: float
    properties: Dict[str, str]
    variables: List[str]
    emoji: str
    doubleOptInEnabled: Optional[bool]
    doubleOptInFrom: Optional[str]
    doubleOptInSubject: Optional[str]
    doubleOptInContent: Optional[str]
    createdAt: str
    updatedAt: str
    _count: ContactBookCounts


ContactBookList = List[ContactBook]


class ContactBookCreate(TypedDict, total=False):
    name: str
    emoji: Optional[str]
    properties: Optional[Dict[str, str]]
    doubleOptInEnabled: Optional[bool]
    doubleOptInFrom: Optional[str]
    doubleOptInSubject: Optional[str]
    doubleOptInContent: Optional[str]
    variables: Optional[List[str]]


class ContactBookCreateResponse(ContactBook, total=False):
    pass


class ContactBookUpdate(TypedDict, total=False):
    name: Optional[str]
    emoji: Optional[str]
    properties: Optional[Dict[str, str]]
    doubleOptInEnabled: Optional[bool]
    doubleOptInFrom: Optional[str]
    doubleOptInSubject: Optional[str]
    doubleOptInContent: Optional[str]
    variables: Optional[List[str]]


class ContactBookUpdateResponse(ContactBook, total=False):
    pass


class ContactBookDeleteResponse(TypedDict):
    id: str
    success: bool
    message: str


class ContactCreate(TypedDict, total=False):
    email: str
    firstName: Optional[str]
    lastName: Optional[str]
    properties: Optional[Dict[str, str]]
    subscribed: Optional[bool]


class ContactCreateResponse(TypedDict, total=False):
    contactId: Optional[str]


class ContactListItem(TypedDict, total=False):
    id: str
    firstName: Optional[str]
    lastName: Optional[str]
    email: str
    subscribed: Optional[bool]
    properties: Dict[str, str]
    contactBookId: str
    createdAt: str
    updatedAt: str


ContactList = List[ContactListItem]


ContactBulkCreate = List[ContactCreate]


class ContactBulkCreateResponse(TypedDict):
    message: str
    count: float


class ContactBulkDelete(TypedDict):
    contactIds: List[str]


class ContactBulkDeleteResponse(TypedDict):
    success: bool
    count: float


class ContactUpdate(TypedDict, total=False):
    firstName: Optional[str]
    lastName: Optional[str]
    properties: Optional[Dict[str, str]]
    subscribed: Optional[bool]


class ContactUpdateResponse(TypedDict, total=False):
    contactId: Optional[str]


class Contact(TypedDict, total=False):
    id: str
    firstName: Optional[str]
    lastName: Optional[str]
    email: str
    subscribed: Optional[bool]
    properties: Dict[str, str]
    contactBookId: str
    createdAt: str
    updatedAt: str


class ContactUpsert(TypedDict, total=False):
    email: str
    firstName: Optional[str]
    lastName: Optional[str]
    properties: Optional[Dict[str, str]]
    subscribed: Optional[bool]


class ContactUpsertResponse(TypedDict):
    contactId: str


class ContactDeleteResponse(TypedDict):
    success: bool


# ---------------------------------------------------------------------------
# Campaigns
# ---------------------------------------------------------------------------

Campaign = TypedDict(
    "Campaign",
    {
        "id": str,
        "name": str,
        "from": str,
        "subject": str,
        "previewText": Optional[str],
        "contactBookId": Optional[str],
        "html": Optional[str],
        "content": Optional[str],
        "status": str,
        "scheduledAt": Optional[str],
        "batchSize": int,
        "batchWindowMinutes": int,
        "total": int,
        "sent": int,
        "delivered": int,
        "opened": int,
        "clicked": int,
        "unsubscribed": int,
        "bounced": int,
        "hardBounced": int,
        "complained": int,
        "replyTo": List[str],
        "cc": List[str],
        "bcc": List[str],
        "createdAt": str,
        "updatedAt": str,
    },
)


CampaignCreate = TypedDict(
    "CampaignCreate",
    {
        "name": Required[str],
        "from": Required[str],
        "subject": Required[str],
        "previewText": NotRequired[str],
        "contactBookId": Required[str],
        "content": NotRequired[str],
        "html": NotRequired[str],
        "replyTo": NotRequired[Union[str, List[str]]],
        "cc": NotRequired[Union[str, List[str]]],
        "bcc": NotRequired[Union[str, List[str]]],
        "sendNow": NotRequired[bool],
        "scheduledAt": NotRequired[str],
        "batchSize": NotRequired[int],
    },
)


CampaignCreateResponse = TypedDict(
    "CampaignCreateResponse",
    {
        "id": str,
        "name": str,
        "from": str,
        "subject": str,
        "previewText": Optional[str],
        "contactBookId": Optional[str],
        "html": Optional[str],
        "content": Optional[str],
        "status": str,
        "scheduledAt": Optional[str],
        "batchSize": int,
        "batchWindowMinutes": int,
        "total": int,
        "sent": int,
        "delivered": int,
        "opened": int,
        "clicked": int,
        "unsubscribed": int,
        "bounced": int,
        "hardBounced": int,
        "complained": int,
        "replyTo": List[str],
        "cc": List[str],
        "bcc": List[str],
        "createdAt": str,
        "updatedAt": str,
    },
)


class CampaignSchedule(TypedDict, total=False):
    scheduledAt: Optional[str]
    batchSize: Optional[int]
    sendNow: Optional[bool]


class CampaignScheduleResponse(TypedDict, total=False):
    success: bool


class CampaignActionResponse(TypedDict, total=False):
    success: bool


# ---------------------------------------------------------------------------
# Common
# ---------------------------------------------------------------------------


class APIError(TypedDict):
    code: str
    message: str


# ---------------------------------------------------------------------------
# Webhook Events
# ---------------------------------------------------------------------------

# Event type literals
ContactWebhookEventType = Literal[
    "contact.created",
    "contact.updated",
    "contact.deleted",
]

DomainWebhookEventType = Literal[
    "domain.created",
    "domain.verified",
    "domain.updated",
    "domain.deleted",
]

EmailWebhookEventType = Literal[
    "email.queued",
    "email.sent",
    "email.delivery_delayed",
    "email.delivered",
    "email.bounced",
    "email.rejected",
    "email.rendering_failure",
    "email.complained",
    "email.failed",
    "email.cancelled",
    "email.suppressed",
    "email.opened",
    "email.clicked",
]

EmailBaseWebhookEventType = Literal[
    "email.queued",
    "email.sent",
    "email.delivery_delayed",
    "email.delivered",
    "email.rejected",
    "email.rendering_failure",
    "email.complained",
    "email.cancelled",
]

WebhookTestEventType = Literal["webhook.test"]

WebhookEventType = Literal[
    # Contact events
    "contact.created",
    "contact.updated",
    "contact.deleted",
    # Domain events
    "domain.created",
    "domain.verified",
    "domain.updated",
    "domain.deleted",
    # Email events
    "email.queued",
    "email.sent",
    "email.delivery_delayed",
    "email.delivered",
    "email.bounced",
    "email.rejected",
    "email.rendering_failure",
    "email.complained",
    "email.failed",
    "email.cancelled",
    "email.suppressed",
    "email.opened",
    "email.clicked",
    # Test event
    "webhook.test",
]

# Email status for webhook payloads
WebhookEmailStatus = Literal[
    "QUEUED",
    "SENT",
    "DELIVERY_DELAYED",
    "DELIVERED",
    "BOUNCED",
    "REJECTED",
    "RENDERING_FAILURE",
    "COMPLAINED",
    "FAILED",
    "CANCELLED",
    "SUPPRESSED",
    "OPENED",
    "CLICKED",
    "SCHEDULED",
]


# Webhook payload types
class EmailBasePayload(TypedDict, total=False):
    """Base payload for email webhook events."""

    id: str
    status: WebhookEmailStatus
    # Note: 'from' is a reserved keyword, using alternative access
    to: List[str]
    occurredAt: str
    campaignId: Optional[str]
    contactId: Optional[str]
    domainId: Optional[int]
    subject: str
    templateId: str
    metadata: Dict[str, Any]


# Using functional syntax for 'from' field
EmailBasePayloadFull = TypedDict(
    "EmailBasePayloadFull",
    {
        "id": str,
        "status": WebhookEmailStatus,
        "from": str,
        "to": List[str],
        "occurredAt": str,
        "campaignId": NotRequired[Optional[str]],
        "contactId": NotRequired[Optional[str]],
        "domainId": NotRequired[Optional[int]],
        "subject": NotRequired[str],
        "templateId": NotRequired[str],
        "metadata": NotRequired[Dict[str, Any]],
    },
)


class ContactWebhookPayload(TypedDict, total=False):
    """Payload for contact webhook events."""

    id: str
    email: str
    contactBookId: str
    subscribed: bool
    properties: Dict[str, Any]
    firstName: Optional[str]
    lastName: Optional[str]
    createdAt: str
    updatedAt: str


class DomainWebhookPayload(TypedDict, total=False):
    """Payload for domain webhook events."""

    id: int
    name: str
    status: str
    region: str
    createdAt: str
    updatedAt: str
    clickTracking: bool
    openTracking: bool
    subdomain: Optional[str]
    sesTenantId: Optional[str]
    dkimStatus: Optional[str]
    spfDetails: Optional[str]
    dmarcAdded: Optional[bool]


BounceType = Literal["Transient", "Permanent", "Undetermined"]
BounceSubType = Literal[
    "General",
    "NoEmail",
    "Suppressed",
    "OnAccountSuppressionList",
    "MailboxFull",
    "MessageTooLarge",
    "ContentRejected",
    "AttachmentRejected",
]


class BounceDetails(TypedDict, total=False):
    """Bounce details for email.bounced events."""

    type: BounceType
    subType: BounceSubType
    message: str


class FailureDetails(TypedDict):
    """Failure details for email.failed events."""

    reason: str


SuppressionType = Literal["Bounce", "Complaint", "Manual"]


class SuppressionDetails(TypedDict, total=False):
    """Suppression details for email.suppressed events."""

    type: SuppressionType
    reason: str
    source: str


class OpenDetails(TypedDict, total=False):
    """Open tracking details for email.opened events."""

    timestamp: str
    userAgent: str
    ip: str
    platform: str


class ClickDetails(TypedDict, total=False):
    """Click tracking details for email.clicked events."""

    timestamp: str
    url: str
    userAgent: str
    ip: str
    platform: str


# Extended email payloads with additional details
EmailBouncedPayload = TypedDict(
    "EmailBouncedPayload",
    {
        "id": str,
        "status": WebhookEmailStatus,
        "from": str,
        "to": List[str],
        "occurredAt": str,
        "campaignId": NotRequired[Optional[str]],
        "contactId": NotRequired[Optional[str]],
        "domainId": NotRequired[Optional[int]],
        "subject": NotRequired[str],
        "templateId": NotRequired[str],
        "metadata": NotRequired[Dict[str, Any]],
        "bounce": BounceDetails,
    },
)

EmailFailedPayload = TypedDict(
    "EmailFailedPayload",
    {
        "id": str,
        "status": WebhookEmailStatus,
        "from": str,
        "to": List[str],
        "occurredAt": str,
        "campaignId": NotRequired[Optional[str]],
        "contactId": NotRequired[Optional[str]],
        "domainId": NotRequired[Optional[int]],
        "subject": NotRequired[str],
        "templateId": NotRequired[str],
        "metadata": NotRequired[Dict[str, Any]],
        "failed": FailureDetails,
    },
)

EmailSuppressedPayload = TypedDict(
    "EmailSuppressedPayload",
    {
        "id": str,
        "status": WebhookEmailStatus,
        "from": str,
        "to": List[str],
        "occurredAt": str,
        "campaignId": NotRequired[Optional[str]],
        "contactId": NotRequired[Optional[str]],
        "domainId": NotRequired[Optional[int]],
        "subject": NotRequired[str],
        "templateId": NotRequired[str],
        "metadata": NotRequired[Dict[str, Any]],
        "suppression": SuppressionDetails,
    },
)

EmailOpenedPayload = TypedDict(
    "EmailOpenedPayload",
    {
        "id": str,
        "status": WebhookEmailStatus,
        "from": str,
        "to": List[str],
        "occurredAt": str,
        "campaignId": NotRequired[Optional[str]],
        "contactId": NotRequired[Optional[str]],
        "domainId": NotRequired[Optional[int]],
        "subject": NotRequired[str],
        "templateId": NotRequired[str],
        "metadata": NotRequired[Dict[str, Any]],
        "open": OpenDetails,
    },
)

EmailClickedPayload = TypedDict(
    "EmailClickedPayload",
    {
        "id": str,
        "status": WebhookEmailStatus,
        "from": str,
        "to": List[str],
        "occurredAt": str,
        "campaignId": NotRequired[Optional[str]],
        "contactId": NotRequired[Optional[str]],
        "domainId": NotRequired[Optional[int]],
        "subject": NotRequired[str],
        "templateId": NotRequired[str],
        "metadata": NotRequired[Dict[str, Any]],
        "click": ClickDetails,
    },
)


class WebhookTestPayload(TypedDict):
    """Payload for webhook.test events."""

    test: bool
    webhookId: str
    sentAt: str


# Webhook event structures
class EmailWebhookEvent(TypedDict):
    """Structure for email webhook events."""

    id: str
    type: EmailBaseWebhookEventType
    createdAt: str
    data: EmailBasePayloadFull


class EmailBouncedEvent(TypedDict):
    """Structure for email.bounced webhook events."""

    id: str
    type: Literal["email.bounced"]
    createdAt: str
    data: EmailBouncedPayload


class EmailFailedEvent(TypedDict):
    """Structure for email.failed webhook events."""

    id: str
    type: Literal["email.failed"]
    createdAt: str
    data: EmailFailedPayload


class EmailSuppressedEvent(TypedDict):
    """Structure for email.suppressed webhook events."""

    id: str
    type: Literal["email.suppressed"]
    createdAt: str
    data: EmailSuppressedPayload


class EmailOpenedEvent(TypedDict):
    """Structure for email.opened webhook events."""

    id: str
    type: Literal["email.opened"]
    createdAt: str
    data: EmailOpenedPayload


class EmailClickedEvent(TypedDict):
    """Structure for email.clicked webhook events."""

    id: str
    type: Literal["email.clicked"]
    createdAt: str
    data: EmailClickedPayload


class ContactWebhookEvent(TypedDict):
    """Structure for contact webhook events."""

    id: str
    type: ContactWebhookEventType
    createdAt: str
    data: ContactWebhookPayload


class DomainWebhookEvent(TypedDict):
    """Structure for domain webhook events."""

    id: str
    type: DomainWebhookEventType
    createdAt: str
    data: DomainWebhookPayload


class WebhookTestEvent(TypedDict):
    """Structure for webhook.test events."""

    id: str
    type: Literal["webhook.test"]
    createdAt: str
    data: WebhookTestPayload


# Union type for all webhook events
WebhookEventData = Union[
    EmailWebhookEvent,
    EmailBouncedEvent,
    EmailFailedEvent,
    EmailSuppressedEvent,
    EmailOpenedEvent,
    EmailClickedEvent,
    ContactWebhookEvent,
    DomainWebhookEvent,
    WebhookTestEvent,
]
