from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models
from django.utils import timezone

from apps.core.choices.fields import RichChoiceField
from apps.core.history import TrackedModel
from apps.core.state_machine import RichFSMField, StateMachineMixin

# Import choices to ensure registration on app load
from . import choices  # noqa: F401


class Ticket(StateMachineMixin, TrackedModel):
    """
    Support ticket model with FSM-managed status transitions.
    
    Status workflow:
        open -> in_progress -> closed
                           -> open (reopen)
    """

    state_field_name = "status"

    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="tickets",
        help_text="User who submitted the ticket (optional)",
    )

    # Submitter info (for anonymous/guest tickets)
    name = models.CharField(
        max_length=255,
        blank=True,
        help_text="Name of the submitter (for anonymous tickets)",
    )

    category = RichChoiceField(
        choice_group="ticket_categories",
        domain="support",
        max_length=20,
        default="general",
        db_index=True,
        help_text="Category of the ticket",
    )
    subject = models.CharField(max_length=255)
    message = models.TextField()
    email = models.EmailField(help_text="Contact email", blank=True)

    # FSM-managed status field
    status = RichFSMField(
        choice_group="ticket_statuses",
        domain="support",
        max_length=20,
        default="open",
        db_index=True,
    )

    # Human-readable ticket number (e.g., TKT-00001)
    ticket_number = models.CharField(
        max_length=20,
        unique=True,
        blank=True,
        help_text="Human-readable ticket number",
    )

    # Admin management fields
    admin_notes = models.TextField(
        blank=True,
        help_text="Internal notes for administrators",
    )
    assigned_to = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="assigned_tickets",
        help_text="Staff member assigned to this ticket",
    )

    # Resolution tracking
    resolved_at = models.DateTimeField(
        null=True,
        blank=True,
        help_text="When the ticket was resolved",
    )
    resolved_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="resolved_tickets",
        help_text="Staff member who resolved this ticket",
    )

    # Archive functionality
    archived_at = models.DateTimeField(
        null=True,
        blank=True,
        help_text="When the ticket was archived",
    )
    archived_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="archived_tickets",
        help_text="Staff member who archived this ticket",
    )

    class Meta(TrackedModel.Meta):
        verbose_name = "Ticket"
        verbose_name_plural = "Tickets"
        ordering = ["-created_at"]
        indexes = [
            models.Index(fields=["status", "created_at"]),
            models.Index(fields=["ticket_number"]),
            models.Index(fields=["archived_at"]),
        ]

    def __str__(self):
        return f"[{self.ticket_number}] {self.subject}"

    def save(self, *args, **kwargs):
        # If user is set but email is empty, autofill from user
        if self.user and not self.email:
            self.email = self.user.email
        # Generate ticket number if not set
        if not self.ticket_number:
            self.ticket_number = self._generate_ticket_number()
        super().save(*args, **kwargs)

    def _generate_ticket_number(self):
        """Generate a unique ticket number like TKT-00001."""
        import uuid

        # Use last 8 chars of a UUID for uniqueness
        suffix = uuid.uuid4().hex[:8].upper()
        return f"TKT-{suffix}"

    # =========================================================================
    # FSM Transition Methods
    # =========================================================================

    def start_progress(self, user=None) -> None:
        """
        Start working on this ticket.
        Transition: open -> in_progress
        
        Args:
            user: The staff member starting work on the ticket
        """
        if self.status != "open":
            raise ValidationError(
                f"Cannot start progress: current status is {self.status}, expected open"
            )
        
        self.status = "in_progress"
        if user and user.is_staff:
            self.assigned_to = user
        self.save()

    def close(self, user=None, notes: str = "") -> None:
        """
        Close/resolve this ticket.
        Transition: in_progress -> closed
        
        Args:
            user: The staff member closing the ticket
            notes: Optional resolution notes
        """
        if self.status not in ("open", "in_progress"):
            raise ValidationError(
                f"Cannot close: current status is {self.status}, expected open or in_progress"
            )
        
        self.status = "closed"
        self.resolved_at = timezone.now()
        if user:
            self.resolved_by = user
        if notes:
            self.admin_notes = f"{self.admin_notes}\n\n[CLOSED] {notes}".strip()
        self.save()

    def reopen(self, user=None, reason: str = "") -> None:
        """
        Reopen a closed ticket.
        Transition: closed -> open
        
        Args:
            user: The staff member reopening the ticket
            reason: Reason for reopening
        """
        if self.status != "closed":
            raise ValidationError(
                f"Cannot reopen: current status is {self.status}, expected closed"
            )
        
        self.status = "open"
        self.resolved_at = None
        self.resolved_by = None
        if reason:
            self.admin_notes = f"{self.admin_notes}\n\n[REOPENED] {reason}".strip()
        self.save()

    def archive(self, user=None, reason: str = "") -> None:
        """
        Archive this ticket.
        Can be called from any status.
        
        Args:
            user: The staff member archiving the ticket
            reason: Reason for archiving
        """
        if self.archived_at:
            raise ValidationError("Ticket is already archived")
        
        self.archived_at = timezone.now()
        if user:
            self.archived_by = user
        if reason:
            self.admin_notes = f"{self.admin_notes}\n\n[ARCHIVED] {reason}".strip()
        self.save()

    def unarchive(self, user=None) -> None:
        """
        Restore an archived ticket.
        
        Args:
            user: The staff member unarchiving the ticket
        """
        if not self.archived_at:
            raise ValidationError("Ticket is not archived")
        
        self.archived_at = None
        self.archived_by = None
        self.admin_notes = f"{self.admin_notes}\n\n[UNARCHIVED] by {user.username if user else 'system'}".strip()
        self.save()

    # =========================================================================
    # Computed Properties
    # =========================================================================

    @property
    def thread_id(self):
        """Return the ID for email thread association."""
        return str(self.id)

    @property
    def response_count(self):
        """Return number of email thread responses."""
        return self.email_threads.count()

    @property
    def last_admin_response_at(self):
        """Return timestamp of last admin response."""
        last_outbound = self.email_threads.filter(direction="outbound").order_by("-created_at").first()
        return last_outbound.created_at if last_outbound else None


class EmailThread(TrackedModel):
    """Email thread entries for ticket conversations."""

    ticket = models.ForeignKey(
        Ticket,
        on_delete=models.CASCADE,
        related_name="email_threads",
        help_text="Associated support ticket",
    )
    message_id = models.CharField(
        max_length=255,
        blank=True,
        help_text="Email message ID for threading",
    )
    from_email = models.EmailField(help_text="Sender email address")
    to_email = models.EmailField(help_text="Recipient email address")
    subject = models.CharField(max_length=255)
    body_text = models.TextField(help_text="Plain text email body")

    direction = RichChoiceField(
        choice_group="email_directions",
        domain="support",
        max_length=10,
        help_text="Whether email is inbound or outbound",
    )
    sent_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="sent_email_threads",
        help_text="Staff member who sent this email (for outbound)",
    )

    class Meta(TrackedModel.Meta):
        verbose_name = "Email Thread"
        verbose_name_plural = "Email Threads"
        ordering = ["created_at"]

    def __str__(self):
        return f"[{self.direction}] {self.subject}"


class Report(TrackedModel):
    """
    User-submitted reports about content issues.
    
    Reports allow users to flag problems with specific entities
    (parks, rides, reviews, etc.) for moderator review.
    """

    # Reporter (optional for anonymous reports)
    reporter = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="submitted_reports",
        help_text="User who submitted the report",
    )

    # Target entity using GenericForeignKey
    content_type = models.ForeignKey(
        "contenttypes.ContentType",
        on_delete=models.CASCADE,
        help_text="Type of entity being reported",
    )
    object_id = models.CharField(
        max_length=50,
        help_text="ID of the entity being reported",
    )
    # Note: GenericForeignKey doesn't create a database column
    # It's a convenience for accessing the related object
    # content_object = GenericForeignKey("content_type", "object_id")

    # Report details - now using RichChoiceField
    report_type = RichChoiceField(
        choice_group="report_types",
        domain="support",
        max_length=20,
        db_index=True,
        help_text="Type of issue being reported",
    )
    reason = models.TextField(
        help_text="Detailed description of the issue",
    )
    status = RichChoiceField(
        choice_group="report_statuses",
        domain="support",
        max_length=20,
        default="pending",
        db_index=True,
        help_text="Current status of the report",
    )

    # Resolution
    resolved_at = models.DateTimeField(
        null=True,
        blank=True,
        help_text="When the report was resolved",
    )
    resolved_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="resolved_reports",
        help_text="Moderator who resolved the report",
    )
    resolution_notes = models.TextField(
        blank=True,
        help_text="Notes about how the report was resolved",
    )

    class Meta(TrackedModel.Meta):
        verbose_name = "Report"
        verbose_name_plural = "Reports"
        ordering = ["-created_at"]
        indexes = [
            models.Index(fields=["status", "created_at"]),
            models.Index(fields=["content_type", "object_id"]),
            models.Index(fields=["report_type", "created_at"]),
        ]

    def __str__(self):
        return f"[{self.get_report_type_display()}] {self.content_type} #{self.object_id}"

    @property
    def is_resolved(self) -> bool:
        return self.status in ("resolved", "dismissed")


