from django.utils import timezone
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import filters, permissions, status, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response

from .models import EmailThread, Report, Ticket
from .serializers import (
    EmailThreadSerializer,
    ReportCreateSerializer,
    ReportResolveSerializer,
    ReportSerializer,
    TicketSerializer,
)


class TicketViewSet(viewsets.ModelViewSet):
    """
    Standard users/guests can CREATE.
    Only Staff can LIST/RETRIEVE/UPDATE all.
    Users can LIST/RETRIEVE their own.
    """

    queryset = Ticket.objects.all()
    serializer_class = TicketSerializer
    permission_classes = [permissions.AllowAny]  # We handle granular perms in get_queryset/perform_create
    filter_backends = [DjangoFilterBackend, filters.OrderingFilter, filters.SearchFilter]
    filterset_fields = ["status", "category", "archived_at"]
    search_fields = ["name", "email", "subject", "ticket_number"]
    ordering_fields = ["created_at", "status", "ticket_number"]
    ordering = ["-created_at"]

    def get_queryset(self):
        user = self.request.user
        qs = Ticket.objects.select_related(
            "user",
            "user__profile",
            "assigned_to",
            "resolved_by",
            "archived_by",
        ).prefetch_related("email_threads")

        if user.is_staff:
            return qs
        if user.is_authenticated:
            return qs.filter(user=user)
        return Ticket.objects.none()  # Guests can't list tickets

    def perform_create(self, serializer):
        if self.request.user.is_authenticated:
            serializer.save(user=self.request.user, email=self.request.user.email)
        else:
            serializer.save()

    @action(detail=True, methods=["post"], permission_classes=[permissions.IsAdminUser])
    def send_reply(self, request, pk=None):
        """
        Send an email reply to the ticket submitter.
        
        Creates an EmailThread record and sends the email via ForwardEmail.
        Optionally updates the ticket status.
        """
        from typing import Any

        from django.conf import settings
        from django.contrib.sites.models import Site

        from django_forwardemail.services import EmailService

        from .serializers import SendReplySerializer

        # Validate input
        serializer = SendReplySerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        validated_data: dict[str, Any] = serializer.validated_data

        ticket = self.get_object()

        if not ticket.email:
            return Response(
                {"detail": "Ticket has no email address to reply to"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        reply_body: str = validated_data["reply_body"]
        new_status: str | None = validated_data.get("new_status")

        # Build email subject with ticket number for threading
        subject = f"Re: [{ticket.ticket_number}] {ticket.subject}"

        # Get the support from email with proper formatting
        from_email: str = getattr(settings, "DEFAULT_FROM_EMAIL", "ThrillWiki Support <support@thrillwiki.com>")

        try:
            # Get the current site for ForwardEmail configuration
            # ForwardEmail requires a Site object, not RequestSite
            try:
                site = Site.objects.get_current()
            except Site.DoesNotExist:
                site = Site.objects.first()
                if site is None:
                    return Response(
                        {"detail": "No site configured for email sending"},
                        status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                    )

            # Send email via ForwardEmail service
            EmailService.send_email(
                to=ticket.email,
                subject=subject,
                text=reply_body,
                from_email=from_email,
                reply_to=from_email,  # Ensure replies come back to support
                site=site,
            )

            # Create EmailThread record for audit trail
            email_thread = EmailThread.objects.create(
                ticket=ticket,
                from_email=from_email,
                to_email=ticket.email,
                subject=subject,
                body_text=reply_body,
                direction="outbound",
                sent_by=request.user,
            )

            # Update ticket status if provided
            if new_status and new_status != ticket.status:
                ticket.status = new_status
                if new_status in ("resolved", "closed"):
                    ticket.resolved_at = timezone.now()
                    ticket.resolved_by = request.user
                ticket.save()

            return Response({
                "detail": "Reply sent successfully",
                "thread_id": str(email_thread.id),
                "ticket_number": ticket.ticket_number,
            })

        except Exception as e:
            # Log the error for debugging
            import logging
            logger = logging.getLogger(__name__)
            logger.exception("Failed to send ticket reply email")
            
            return Response(
                {"detail": f"Failed to send email: {str(e)}"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR,
            )

    @action(detail=False, methods=["post"], permission_classes=[permissions.IsAdminUser])
    def merge(self, request):
        """
        Merge multiple tickets into a primary ticket.
        
        Moves all EmailThread records to the primary ticket and archives merged tickets.
        """
        from typing import Any
        from uuid import UUID

        from .serializers import MergeTicketsSerializer

        # Validate input
        serializer = MergeTicketsSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        validated_data: dict[str, Any] = serializer.validated_data

        primary_id: UUID = validated_data["primary_ticket_id"]
        merge_ids: list[UUID] = validated_data["merge_ticket_ids"]
        reason: str = validated_data.get("merge_reason", "")

        # Get primary ticket
        try:
            primary_ticket = Ticket.objects.get(pk=primary_id)
        except Ticket.DoesNotExist:
            return Response(
                {"detail": f"Primary ticket {primary_id} not found"},
                status=status.HTTP_404_NOT_FOUND,
            )

        # Get tickets to merge (exclud primary if accidentally included)
        tickets_to_merge = Ticket.objects.filter(pk__in=merge_ids).exclude(pk=primary_id)
        if tickets_to_merge.count() == 0:
            return Response(
                {"detail": "No valid tickets found to merge"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        merged_count = 0
        threads_consolidated = 0
        merged_ticket_numbers: list[str] = []

        for ticket in tickets_to_merge:
            # Move all email threads to primary ticket
            thread_count = EmailThread.objects.filter(ticket=ticket).update(ticket=primary_ticket)
            threads_consolidated += thread_count

            # Record the merged ticket number
            merged_ticket_numbers.append(ticket.ticket_number)

            # Archive the merged ticket with merge history
            ticket.archived_at = timezone.now()
            ticket.archived_by = request.user
            existing_notes = ticket.admin_notes or ""
            ticket.admin_notes = (
                f"{existing_notes}\n\n"
                f"[MERGED] Merged into {primary_ticket.ticket_number} by {request.user.username} "
                f"on {timezone.now().isoformat()}. Reason: {reason or 'Not specified'}"
            ).strip()
            ticket.save()

            merged_count += 1

        # Update primary ticket with merge history
        existing_merged = primary_ticket.admin_notes or ""
        merge_note = (
            f"\n\n[MERGE HISTORY] Absorbed tickets: {', '.join(merged_ticket_numbers)} "
            f"({threads_consolidated} threads consolidated) by {request.user.username}"
        )
        primary_ticket.admin_notes = existing_merged + merge_note
        primary_ticket.save()

        return Response({
            "detail": "Tickets merged successfully",
            "primaryTicketNumber": primary_ticket.ticket_number,
            "mergedCount": merged_count,
            "threadsConsolidated": threads_consolidated,
        })

    # =========================================================================
    # FSM Transition Endpoints
    # =========================================================================

    @action(detail=True, methods=["post"], permission_classes=[permissions.IsAdminUser])
    def start_progress(self, request, pk=None):
        """
        Start working on a ticket.
        Transition: open -> in_progress
        """
        from django.core.exceptions import ValidationError as DjangoValidationError

        ticket = self.get_object()
        try:
            ticket.start_progress(user=request.user)
            return Response({
                "detail": "Ticket marked as in progress",
                "ticketNumber": ticket.ticket_number,
                "status": ticket.status,
            })
        except DjangoValidationError as e:
            return Response(
                {"detail": str(e.message if hasattr(e, 'message') else e)},
                status=status.HTTP_400_BAD_REQUEST,
            )

    @action(detail=True, methods=["post"], permission_classes=[permissions.IsAdminUser], url_path="close")
    def close_ticket(self, request, pk=None):
        """
        Close/resolve a ticket.
        Transition: open|in_progress -> closed
        """
        from django.core.exceptions import ValidationError as DjangoValidationError

        ticket = self.get_object()
        notes = request.data.get("notes", "")

        try:
            ticket.close(user=request.user, notes=notes)
            return Response({
                "detail": "Ticket closed successfully",
                "ticketNumber": ticket.ticket_number,
                "status": ticket.status,
                "resolvedAt": ticket.resolved_at.isoformat() if ticket.resolved_at else None,
            })
        except DjangoValidationError as e:
            return Response(
                {"detail": str(e.message if hasattr(e, 'message') else e)},
                status=status.HTTP_400_BAD_REQUEST,
            )

    @action(detail=True, methods=["post"], permission_classes=[permissions.IsAdminUser])
    def reopen(self, request, pk=None):
        """
        Reopen a closed ticket.
        Transition: closed -> open
        """
        from django.core.exceptions import ValidationError as DjangoValidationError

        ticket = self.get_object()
        reason = request.data.get("reason", "")

        try:
            ticket.reopen(user=request.user, reason=reason)
            return Response({
                "detail": "Ticket reopened",
                "ticketNumber": ticket.ticket_number,
                "status": ticket.status,
            })
        except DjangoValidationError as e:
            return Response(
                {"detail": str(e.message if hasattr(e, 'message') else e)},
                status=status.HTTP_400_BAD_REQUEST,
            )

    @action(detail=True, methods=["post"], permission_classes=[permissions.IsAdminUser])
    def archive(self, request, pk=None):
        """Archive a ticket."""
        from django.core.exceptions import ValidationError as DjangoValidationError

        ticket = self.get_object()
        reason = request.data.get("reason", "")

        try:
            ticket.archive(user=request.user, reason=reason)
            return Response({
                "detail": "Ticket archived",
                "ticketNumber": ticket.ticket_number,
                "archivedAt": ticket.archived_at.isoformat() if ticket.archived_at else None,
            })
        except DjangoValidationError as e:
            return Response(
                {"detail": str(e.message if hasattr(e, 'message') else e)},
                status=status.HTTP_400_BAD_REQUEST,
            )

    @action(detail=True, methods=["post"], permission_classes=[permissions.IsAdminUser])
    def unarchive(self, request, pk=None):
        """Restore an archived ticket."""
        from django.core.exceptions import ValidationError as DjangoValidationError

        ticket = self.get_object()

        try:
            ticket.unarchive(user=request.user)
            return Response({
                "detail": "Ticket unarchived",
                "ticketNumber": ticket.ticket_number,
            })
        except DjangoValidationError as e:
            return Response(
                {"detail": str(e.message if hasattr(e, 'message') else e)},
                status=status.HTTP_400_BAD_REQUEST,
            )

    @action(detail=True, methods=["get"], permission_classes=[permissions.IsAdminUser])
    def available_transitions(self, request, pk=None):
        """
        Get available transitions for a ticket.
        Uses StateMachineMixin to return FSM-aware transition metadata.
        """
        ticket = self.get_object()
        transitions = ticket.get_available_user_transitions(request.user)

        return Response({
            "ticketNumber": ticket.ticket_number,
            "currentStatus": ticket.status,
            "currentStatusDisplay": ticket.get_status_display(),
            "availableTransitions": transitions,
        })


class EmailThreadViewSet(viewsets.ModelViewSet):
    """
    ViewSet for email thread entries.
    Staff only for full access.
    """

    queryset = EmailThread.objects.select_related("ticket", "sent_by").all()
    serializer_class = EmailThreadSerializer
    permission_classes = [permissions.IsAdminUser]
    filter_backends = [DjangoFilterBackend, filters.OrderingFilter]
    filterset_fields = ["ticket", "direction"]
    ordering_fields = ["created_at"]
    ordering = ["created_at"]

    def get_queryset(self):
        # Support filtering by submission_id (which is ticket_id in our model)
        qs = super().get_queryset()
        submission_id = self.request.query_params.get("submission_id")
        if submission_id:
            qs = qs.filter(ticket_id=submission_id)
        return qs


class ReportViewSet(viewsets.ModelViewSet):
    """
    ViewSet for handling user-submitted content reports.
    
    - Authenticated users can CREATE reports
    - Staff can LIST/RETRIEVE all reports
    - Users can LIST/RETRIEVE their own reports
    - Staff can RESOLVE reports
    """

    queryset = Report.objects.select_related("reporter", "resolved_by", "content_type").all()
    permission_classes = [permissions.IsAuthenticated]
    filter_backends = [DjangoFilterBackend, filters.OrderingFilter, filters.SearchFilter]
    filterset_fields = ["status", "report_type"]
    search_fields = ["reason", "resolution_notes"]
    ordering_fields = ["created_at", "status", "report_type"]
    ordering = ["-created_at"]

    def get_serializer_class(self):
        if self.action == "create":
            return ReportCreateSerializer
        if self.action == "resolve":
            return ReportResolveSerializer
        return ReportSerializer

    def get_queryset(self):
        user = self.request.user
        if user.is_staff:
            return Report.objects.select_related("reporter", "resolved_by", "content_type").all()
        return Report.objects.select_related("reporter", "resolved_by", "content_type").filter(reporter=user)

    def perform_create(self, serializer):
        serializer.save(reporter=self.request.user)

    @action(detail=True, methods=["post"], permission_classes=[permissions.IsAdminUser])
    def resolve(self, request, pk=None):
        """Mark a report as resolved or dismissed."""
        report = self.get_object()

        if report.is_resolved:
            return Response(
                {"detail": "Report is already resolved"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        serializer = ReportResolveSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        report.status = serializer.validated_data.get("status", "resolved")
        report.resolved_at = timezone.now()
        report.resolved_by = request.user
        report.resolution_notes = serializer.validated_data.get("notes", "")
        report.save()

        return Response(ReportSerializer(report).data)

