"""Message data models - drafts and sent messages."""

from __future__ import annotations
from dataclasses import dataclass, field
from datetime import datetime
from typing import Optional
import uuid


@dataclass
class MessageDraft:
    """
    Represents a message draft before sending.
    """
    id: str = field(default_factory=lambda: str(uuid.uuid4()))
    contact_id: str = ""
    sequence_id: Optional[str] = None
    step_id: Optional[str] = None

    # Content
    channel: str = "email"  # email | linkedin | call
    subject: str = ""
    body: str = ""

    # Personalization
    personalization_context: dict = field(default_factory=dict)
    first_line: Optional[str] = None
    variables_used: list[str] = field(default_factory=list)

    # A/B testing
    variant_index: int = 0

    # Status
    approved: bool = False
    approved_by: Optional[str] = None
    approved_at: Optional[datetime] = None

    # Scheduling
    scheduled_at: Optional[datetime] = None

    # Timestamps
    created_at: datetime = field(default_factory=datetime.now)
    updated_at: datetime = field(default_factory=datetime.now)

    def to_dict(self) -> dict:
        return {
            "id": self.id,
            "contact_id": self.contact_id,
            "sequence_id": self.sequence_id,
            "step_id": self.step_id,
            "channel": self.channel,
            "subject": self.subject,
            "body": self.body,
            "personalization_context": self.personalization_context,
            "first_line": self.first_line,
            "variables_used": self.variables_used,
            "variant_index": self.variant_index,
            "approved": self.approved,
            "approved_by": self.approved_by,
            "approved_at": self.approved_at.isoformat() if self.approved_at else None,
            "scheduled_at": self.scheduled_at.isoformat() if self.scheduled_at else None,
            "created_at": self.created_at.isoformat(),
            "updated_at": self.updated_at.isoformat(),
        }

    @classmethod
    def from_dict(cls, data: dict) -> MessageDraft:
        data = data.copy()
        for field_name in ["approved_at", "scheduled_at", "created_at", "updated_at"]:
            if field_name in data and isinstance(data[field_name], str):
                data[field_name] = datetime.fromisoformat(data[field_name])
        return cls(**data)

    def approve(self, approved_by: str = "system") -> None:
        """Approve the draft for sending."""
        self.approved = True
        self.approved_by = approved_by
        self.approved_at = datetime.now()
        self.updated_at = datetime.now()


@dataclass
class SentMessage:
    """
    Represents a message that has been sent.
    """
    id: str = field(default_factory=lambda: str(uuid.uuid4()))
    draft_id: Optional[str] = None
    contact_id: str = ""
    sequence_id: Optional[str] = None
    step_id: Optional[str] = None

    # Content (captured at send time)
    channel: str = "email"
    to_email: str = ""
    from_email: str = ""
    subject: str = ""
    body: str = ""

    # Tracking
    message_id: Optional[str] = None  # Email provider message ID
    thread_id: Optional[str] = None

    # Events
    sent_at: datetime = field(default_factory=datetime.now)
    delivered_at: Optional[datetime] = None
    opened_at: Optional[datetime] = None
    clicked_at: Optional[datetime] = None
    replied_at: Optional[datetime] = None

    # Open/click tracking
    open_count: int = 0
    click_count: int = 0
    clicked_links: list[str] = field(default_factory=list)

    # Delivery status
    status: str = "sent"  # sent | delivered | opened | replied | bounced | failed
    bounced: bool = False
    bounce_type: Optional[str] = None  # hard | soft
    bounce_reason: Optional[str] = None

    # A/B variant tracking
    variant_index: int = 0

    def to_dict(self) -> dict:
        return {
            "id": self.id,
            "draft_id": self.draft_id,
            "contact_id": self.contact_id,
            "sequence_id": self.sequence_id,
            "step_id": self.step_id,
            "channel": self.channel,
            "to_email": self.to_email,
            "from_email": self.from_email,
            "subject": self.subject,
            "body": self.body,
            "message_id": self.message_id,
            "thread_id": self.thread_id,
            "sent_at": self.sent_at.isoformat(),
            "delivered_at": self.delivered_at.isoformat() if self.delivered_at else None,
            "opened_at": self.opened_at.isoformat() if self.opened_at else None,
            "clicked_at": self.clicked_at.isoformat() if self.clicked_at else None,
            "replied_at": self.replied_at.isoformat() if self.replied_at else None,
            "open_count": self.open_count,
            "click_count": self.click_count,
            "clicked_links": self.clicked_links,
            "status": self.status,
            "bounced": self.bounced,
            "bounce_type": self.bounce_type,
            "bounce_reason": self.bounce_reason,
            "variant_index": self.variant_index,
        }

    @classmethod
    def from_dict(cls, data: dict) -> SentMessage:
        data = data.copy()
        for field_name in ["sent_at", "delivered_at", "opened_at", "clicked_at", "replied_at"]:
            if field_name in data and isinstance(data[field_name], str):
                data[field_name] = datetime.fromisoformat(data[field_name])
        return cls(**data)

    def record_open(self) -> None:
        """Record an email open."""
        if not self.opened_at:
            self.opened_at = datetime.now()
        self.open_count += 1
        if self.status == "delivered":
            self.status = "opened"

    def record_click(self, link: str) -> None:
        """Record a link click."""
        if not self.clicked_at:
            self.clicked_at = datetime.now()
        self.click_count += 1
        if link not in self.clicked_links:
            self.clicked_links.append(link)

    def record_reply(self) -> None:
        """Record a reply."""
        self.replied_at = datetime.now()
        self.status = "replied"

    def record_bounce(self, bounce_type: str, reason: str) -> None:
        """Record a bounce."""
        self.bounced = True
        self.bounce_type = bounce_type
        self.bounce_reason = reason
        self.status = "bounced"
