"""Webhook event types and domain entities.

Domain entities match the ORM models in
infrastructure/persistence/models/integrations/webhook.py for seamless conversion.
"""

from __future__ import annotations

from dataclasses import asdict, dataclass, field
from datetime import datetime
from enum import Enum
from typing import TYPE_CHECKING
from uuid import UUID, uuid4

from noteflow.domain.utils.time import utc_now
from noteflow.domain.webhooks.constants import (
    DEFAULT_WEBHOOK_MAX_RETRIES,
    DEFAULT_WEBHOOK_TIMEOUT_MS,
)

# Type alias for JSON-serializable webhook payload values
# Webhook payloads use flat structures with primitive types
type WebhookPayloadValue = str | int | float | bool | None
type WebhookPayloadDict = dict[str, WebhookPayloadValue]

if TYPE_CHECKING:
    from typing import TypeVar

    _PayloadT = TypeVar("_PayloadT", bound="WebhookPayload")


def payload_to_dict(payload: WebhookPayload) -> WebhookPayloadDict:
    """Convert webhook payload dataclass to typed dictionary.

    Uses dataclasses.asdict() for conversion, filtering out None values
    to keep payloads compact.

    Args:
        payload: Any WebhookPayload subclass instance.

    Returns:
        Dictionary with non-None field values.
    """
    return {k: v for k, v in asdict(payload).items() if v is not None}


class WebhookEventType(Enum):
    """Types of webhook trigger events."""

    MEETING_COMPLETED = "meeting.completed"
    SUMMARY_GENERATED = "summary.generated"
    RECORDING_STARTED = "recording.started"
    RECORDING_STOPPED = "recording.stopped"


@dataclass(frozen=True, slots=True)
class WebhookConfig:
    """Webhook configuration for event delivery.

    Fields match WebhookConfigModel ORM for seamless conversion.

    Attributes:
        id: Unique webhook identifier.
        workspace_id: Workspace this webhook belongs to.
        url: Target URL for webhook delivery.
        events: Set of event types this webhook is subscribed to.
        name: Display name for the webhook.
        secret: Optional HMAC signing secret.
        enabled: Whether the webhook is active.
        timeout_ms: HTTP request timeout in milliseconds.
        max_retries: Maximum delivery retry attempts.
        created_at: When the webhook was created.
        updated_at: When the webhook was last modified.
    """

    id: UUID
    workspace_id: UUID
    url: str
    events: frozenset[WebhookEventType]
    name: str = "Webhook"
    secret: str | None = None
    enabled: bool = True
    timeout_ms: int = DEFAULT_WEBHOOK_TIMEOUT_MS
    max_retries: int = DEFAULT_WEBHOOK_MAX_RETRIES
    created_at: datetime = field(default_factory=utc_now)
    updated_at: datetime = field(default_factory=utc_now)

    @classmethod
    def create(
        cls,
        workspace_id: UUID,
        url: str,
        events: list[WebhookEventType],
        *,
        name: str = "Webhook",
        secret: str | None = None,
        timeout_ms: int = DEFAULT_WEBHOOK_TIMEOUT_MS,
        max_retries: int = DEFAULT_WEBHOOK_MAX_RETRIES,
    ) -> WebhookConfig:
        """Create a new webhook configuration.

        Args:
            workspace_id: Workspace UUID.
            url: Target URL for delivery.
            events: List of event types to subscribe.
            name: Display name.
            secret: Optional HMAC signing secret.
            timeout_ms: Request timeout in milliseconds.
            max_retries: Maximum retry attempts.

        Returns:
            New WebhookConfig with generated ID and timestamps.
        """
        now = utc_now()
        return cls(
            id=uuid4(),
            workspace_id=workspace_id,
            url=url,
            events=frozenset(events),
            name=name,
            secret=secret,
            timeout_ms=timeout_ms,
            max_retries=max_retries,
            created_at=now,
            updated_at=now,
        )

    def subscribes_to(self, event_type: WebhookEventType) -> bool:
        """Check if this webhook subscribes to the given event type.

        Args:
            event_type: Event type to check.

        Returns:
            True if subscribed to this event.
        """
        return event_type in self.events


@dataclass(frozen=True, slots=True)
class DeliveryResult:
    """Result of a webhook delivery attempt.

    Groups delivery outcome fields to reduce parameter count.
    """

    status_code: int | None = None
    """HTTP response status code (None if request failed)."""

    response_body: str | None = None
    """Response body (truncated if large)."""

    error_message: str | None = None
    """Error description if delivery failed."""

    attempt_count: int = 1
    """Number of delivery attempts made."""

    duration_ms: int | None = None
    """Request duration in milliseconds."""


@dataclass(frozen=True, slots=True)
class WebhookDelivery:
    """Record of a webhook delivery attempt.

    Fields match WebhookDeliveryModel ORM for seamless conversion.

    Attributes:
        id: Unique delivery identifier.
        webhook_id: Associated webhook config ID.
        event_type: Type of event that triggered delivery.
        payload: Event payload that was sent.
        status_code: HTTP response status code (None if request failed).
        response_body: Response body (truncated if large).
        error_message: Error description if delivery failed.
        attempt_count: Number of delivery attempts made.
        duration_ms: Request duration in milliseconds.
        delivered_at: When the delivery was attempted.
    """

    id: UUID
    webhook_id: UUID
    event_type: WebhookEventType
    payload: WebhookPayloadDict
    status_code: int | None
    response_body: str | None
    error_message: str | None
    attempt_count: int
    duration_ms: int | None
    delivered_at: datetime

    @classmethod
    def create(
        cls,
        webhook_id: UUID,
        event_type: WebhookEventType,
        payload: WebhookPayloadDict,
        result: DeliveryResult | None = None,
    ) -> WebhookDelivery:
        """Create a new delivery record.

        Args:
            webhook_id: Associated webhook config ID.
            event_type: Type of event.
            payload: Event payload.
            result: Optional delivery result (status, response, etc.).

        Returns:
            New WebhookDelivery with generated ID and timestamp.
        """
        r = result or DeliveryResult()
        return cls(
            id=uuid4(),
            webhook_id=webhook_id,
            event_type=event_type,
            payload=payload,
            status_code=r.status_code,
            response_body=r.response_body,
            error_message=r.error_message,
            attempt_count=r.attempt_count,
            duration_ms=r.duration_ms,
            delivered_at=utc_now(),
        )

    @property
    def succeeded(self) -> bool:
        """Check if delivery was successful.

        Returns:
            True if status code indicates success (2xx).
        """
        return self.status_code is not None and 200 <= self.status_code < 300


@dataclass(frozen=True, slots=True)
class WebhookPayload:
    """Base webhook event payload.

    Use payload_to_dict() helper for JSON serialization.

    Attributes:
        event: Event type identifier string.
        timestamp: ISO 8601 formatted event timestamp.
        meeting_id: Associated meeting UUID as string.
    """

    event: str
    timestamp: str
    meeting_id: str


@dataclass(frozen=True, slots=True)
class MeetingCompletedPayload(WebhookPayload):
    """Payload for meeting.completed event.

    Attributes:
        title: Meeting title.
        duration_seconds: Total meeting duration.
        segment_count: Number of transcript segments.
        has_summary: Whether a summary exists.
    """

    title: str
    duration_seconds: float
    segment_count: int
    has_summary: bool


@dataclass(frozen=True, slots=True)
class SummaryGeneratedPayload(WebhookPayload):
    """Payload for summary.generated event.

    Attributes:
        title: Meeting title.
        executive_summary: Summary executive overview text.
        key_points_count: Number of key points in summary.
        action_items_count: Number of action items in summary.
    """

    title: str
    executive_summary: str
    key_points_count: int
    action_items_count: int


@dataclass(frozen=True, slots=True)
class RecordingPayload(WebhookPayload):
    """Payload for recording.started and recording.stopped events.

    Attributes:
        title: Meeting title.
        duration_seconds: Recording duration (only for stopped events).
    """

    title: str
    duration_seconds: float | None = None
