"""
Moderation API Views

This module contains DRF viewsets for the moderation system, including:
- ModerationReport views for content reporting
- ModerationQueue views for moderation workflow
- ModerationAction views for tracking moderation actions
- BulkOperation views for administrative bulk operations

All views include comprehensive permissions, filtering, and pagination.
"""

import logging
from datetime import timedelta

from django.contrib.auth import get_user_model
from django.core.paginator import Paginator
from django.db.models import Count, Q
from django.shortcuts import render
from django.utils import timezone
from django_filters.rest_framework import DjangoFilterBackend
from django_fsm import TransitionNotAllowed, can_proceed
from rest_framework import permissions, status, viewsets
from rest_framework.decorators import action
from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.response import Response

from apps.core.logging import log_business_event
from apps.core.state_machine.exceptions import (
    TransitionPermissionDenied,
    TransitionValidationError,
    format_transition_error,
)

from .filters import (
    BulkOperationFilter,
    ModerationActionFilter,
    ModerationQueueFilter,
    ModerationReportFilter,
)
from .models import (
    BulkOperation,
    EditSubmission,
    ModerationAction,
    ModerationQueue,
    ModerationReport,
    PhotoSubmission,
)
from .permissions import (
    CanViewModerationData,
    IsAdminOrSuperuser,
    IsModeratorOrAdmin,
)
from .serializers import (
    AssignQueueItemSerializer,
    BulkOperationSerializer,
    CompleteQueueItemSerializer,
    CreateBulkOperationSerializer,
    CreateEditSubmissionSerializer,
    CreateModerationActionSerializer,
    CreateModerationReportSerializer,
    EditSubmissionListSerializer,
    EditSubmissionSerializer,
    ModerationActionSerializer,
    ModerationQueueSerializer,
    ModerationReportSerializer,
    PhotoSubmissionSerializer,
    UpdateModerationReportSerializer,
    UserModerationProfileSerializer,
)

User = get_user_model()

logger = logging.getLogger(__name__)


# ============================================================================
# Moderation Report ViewSet
# ============================================================================


class ModerationReportViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing moderation reports.

    Provides CRUD operations for moderation reports with comprehensive
    filtering, search, and permission controls.
    """

    queryset = ModerationReport.objects.select_related("reported_by", "assigned_moderator", "content_type").all()

    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_class = ModerationReportFilter
    search_fields = ["reason", "description", "resolution_notes"]
    ordering_fields = ["created_at", "updated_at", "priority", "status"]
    ordering = ["-created_at"]

    def get_serializer_class(self):
        """Return appropriate serializer based on action."""
        if self.action == "create":
            return CreateModerationReportSerializer
        elif self.action in ["update", "partial_update"]:
            return UpdateModerationReportSerializer
        return ModerationReportSerializer

    def get_permissions(self):
        """Return appropriate permissions based on action."""
        if self.action == "create":
            # Any authenticated user can create reports
            permission_classes = [permissions.IsAuthenticated]
        elif self.action in ["list", "retrieve"]:
            # Moderators and above can view reports
            permission_classes = [CanViewModerationData]
        else:
            # Only moderators and above can modify reports
            permission_classes = [IsModeratorOrAdmin]

        return [permission() for permission in permission_classes]

    def get_queryset(self):
        """Filter queryset based on user permissions."""
        queryset = super().get_queryset()

        # Regular users can only see their own reports
        if not self.request.user.is_authenticated:
            return queryset.none()

        user_role = getattr(self.request.user, "role", "USER")
        if user_role == "USER":
            queryset = queryset.filter(reported_by=self.request.user)

        return queryset

    @action(detail=True, methods=["post"], permission_classes=[IsModeratorOrAdmin])
    def assign(self, request, pk=None):
        """Assign a report to a moderator."""
        report = self.get_object()
        moderator_id = request.data.get("moderator_id")

        try:
            moderator = User.objects.get(id=moderator_id)
            moderator_role = getattr(moderator, "role", "USER")

            if moderator_role not in ["MODERATOR", "ADMIN", "SUPERUSER"]:
                return Response(
                    {"error": "User must be a moderator, admin, or superuser"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            # Check if transition method exists
            transition_method = getattr(report, "transition_to_under_review", None)
            if transition_method is None:
                return Response(
                    {"error": "Transition method not available"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            # Check if transition can proceed before attempting
            if not can_proceed(transition_method, moderator):
                return Response(
                    format_transition_error(
                        TransitionPermissionDenied(
                            message="Cannot transition to UNDER_REVIEW",
                            user_message="You don't have permission to assign this report or it cannot be transitioned from the current state.",
                        )
                    ),
                    status=status.HTTP_403_FORBIDDEN,
                )

            report.assigned_moderator = moderator
            old_status = report.status
            try:
                transition_method(user=moderator)
                report.save()
                log_business_event(
                    logger,
                    event_type="fsm_transition",
                    message=f"ModerationReport {report.id} assigned to {moderator.username}",
                    context={
                        "model": "ModerationReport",
                        "object_id": report.id,
                        "old_state": old_status,
                        "new_state": report.status,
                        "transition": "assign",
                        "moderator": moderator.username,
                    },
                    request=request,
                )
            except TransitionPermissionDenied as e:
                return Response(
                    format_transition_error(e),
                    status=status.HTTP_403_FORBIDDEN,
                )
            except TransitionValidationError as e:
                return Response(
                    format_transition_error(e),
                    status=status.HTTP_400_BAD_REQUEST,
                )
            except TransitionNotAllowed as e:
                return Response(
                    format_transition_error(e),
                    status=status.HTTP_400_BAD_REQUEST,
                )

            serializer = self.get_serializer(report)
            return Response(serializer.data)

        except User.DoesNotExist:
            return Response({"error": "Moderator not found"}, status=status.HTTP_404_NOT_FOUND)

    @action(detail=True, methods=["post"], permission_classes=[IsModeratorOrAdmin])
    def resolve(self, request, pk=None):
        """Resolve a moderation report."""
        report = self.get_object()

        resolution_action = request.data.get("resolution_action")
        resolution_notes = request.data.get("resolution_notes", "")

        if not resolution_action:
            return Response(
                {"error": "resolution_action is required"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        # Check if transition method exists
        transition_method = getattr(report, "transition_to_resolved", None)
        if transition_method is None:
            return Response(
                {"error": "Transition method not available"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        # Check if transition can proceed before attempting
        if not can_proceed(transition_method, request.user):
            return Response(
                format_transition_error(
                    TransitionPermissionDenied(
                        message="Cannot transition to RESOLVED",
                        user_message="You don't have permission to resolve this report or it cannot be resolved from the current state.",
                    )
                ),
                status=status.HTTP_403_FORBIDDEN,
            )

        old_status = report.status
        try:
            transition_method(user=request.user)
        except TransitionPermissionDenied as e:
            return Response(
                format_transition_error(e),
                status=status.HTTP_403_FORBIDDEN,
            )
        except TransitionValidationError as e:
            return Response(
                format_transition_error(e),
                status=status.HTTP_400_BAD_REQUEST,
            )
        except TransitionNotAllowed as e:
            return Response(
                format_transition_error(e),
                status=status.HTTP_400_BAD_REQUEST,
            )

        report.resolution_action = resolution_action
        report.resolution_notes = resolution_notes
        report.resolved_at = timezone.now()
        report.save()

        log_business_event(
            logger,
            event_type="fsm_transition",
            message=f"ModerationReport {report.id} resolved with action: {resolution_action}",
            context={
                "model": "ModerationReport",
                "object_id": report.id,
                "old_state": old_status,
                "new_state": report.status,
                "transition": "resolve",
                "resolution_action": resolution_action,
                "user": request.user.username,
            },
            request=request,
        )

        serializer = self.get_serializer(report)
        return Response(serializer.data)

    @action(detail=False, methods=["get"], permission_classes=[CanViewModerationData])
    def stats(self, request):
        """Get moderation report statistics."""
        queryset = self.get_queryset()

        # Basic counts
        total_reports = queryset.count()
        pending_reports = queryset.filter(status="PENDING").count()
        resolved_reports = queryset.filter(status="RESOLVED").count()

        # Overdue reports (based on priority SLA)
        now = timezone.now()
        overdue_reports = 0

        for report in queryset.filter(status__in=["PENDING", "UNDER_REVIEW"]):
            sla_hours = {"URGENT": 2, "HIGH": 8, "MEDIUM": 24, "LOW": 72}
            hours_since_created = (now - report.created_at).total_seconds() / 3600
            if report.priority in sla_hours:
                threshold = sla_hours[report.priority]
            else:
                raise ValueError(f"Unknown priority level: {report.priority}")
            if hours_since_created > threshold:
                overdue_reports += 1

        # Reports by priority and type
        reports_by_priority = dict(queryset.values_list("priority").annotate(count=Count("id")))
        reports_by_type = dict(queryset.values_list("report_type").annotate(count=Count("id")))

        # Average resolution time
        resolved_queryset = queryset.filter(status="RESOLVED", resolved_at__isnull=False)

        avg_resolution_time = 0
        if resolved_queryset.exists():
            total_time = sum(
                [
                    (report.resolved_at - report.created_at).total_seconds() / 3600
                    for report in resolved_queryset
                    if report.resolved_at
                ]
            )
            avg_resolution_time = total_time / resolved_queryset.count()

        stats_data = {
            "total_reports": total_reports,
            "pending_reports": pending_reports,
            "resolved_reports": resolved_reports,
            "overdue_reports": overdue_reports,
            "reports_by_priority": reports_by_priority,
            "reports_by_type": reports_by_type,
            "average_resolution_time_hours": round(avg_resolution_time, 2),
        }

        return Response(stats_data)

    @action(detail=True, methods=["get"], permission_classes=[CanViewModerationData])
    def history(self, request, pk=None):
        """Get transition history for this report."""
        from django.contrib.contenttypes.models import ContentType
        from django_fsm_log.models import StateLog

        report = self.get_object()
        content_type = ContentType.objects.get_for_model(report)

        logs = (
            StateLog.objects.filter(content_type=content_type, object_id=report.id)
            .select_related("by")
            .order_by("-timestamp")
        )

        history_data = [
            {
                "id": log.id,
                "timestamp": log.timestamp,
                "state": log.state,
                "from_state": log.source_state,
                "to_state": log.state,
                "transition": log.transition,
                "user": log.by.username if log.by else None,
                "description": log.description,
                "reason": log.description,
            }
            for log in logs
        ]

        return Response(history_data)

    @action(detail=False, methods=["get"], permission_classes=[CanViewModerationData])
    def all_history(self, request):
        """Get all transition history with filtering.

        Supports both HTMX (returns HTML partials) and API (returns JSON) requests.
        """
        from django.contrib.contenttypes.models import ContentType
        from django_fsm_log.models import StateLog

        queryset = StateLog.objects.select_related("by", "content_type").all()

        # Filter by id (for detail view)
        log_id = request.query_params.get("id")
        if log_id:
            try:
                log = queryset.get(id=log_id)
                # Check if HTMX request for detail view
                if request.headers.get("HX-Request"):
                    return render(
                        request,
                        "moderation/partials/history_detail_content.html",
                        {
                            "log": log,
                        },
                    )
                # Return JSON for API request
                return Response(
                    {
                        "id": log.id,
                        "timestamp": log.timestamp,
                        "model": log.content_type.model,
                        "object_id": log.object_id,
                        "state": log.state,
                        "from_state": log.source_state,
                        "to_state": log.state,
                        "transition": log.transition,
                        "user": log.by.username if log.by else None,
                        "description": log.description,
                        "reason": log.description,
                    }
                )
            except StateLog.DoesNotExist:
                if request.headers.get("HX-Request"):
                    return render(
                        request,
                        "moderation/partials/history_detail_content.html",
                        {
                            "log": None,
                        },
                    )
                return Response({"error": "Log not found"}, status=status.HTTP_404_NOT_FOUND)

        # Filter by model type with app_label support for correct ContentType resolution
        model_type = request.query_params.get("model_type")
        app_label = request.query_params.get("app_label")
        if model_type:
            try:
                if app_label:
                    # Use both app_label and model for precise matching
                    content_type = ContentType.objects.get_by_natural_key(app_label, model_type)
                else:
                    # Map common model names to their app_labels for correct resolution
                    model_app_mapping = {
                        "park": "parks",
                        "ride": "rides",
                        "editsubmission": "submissions",
                        "photosubmission": "submissions",
                        "moderationreport": "moderation",
                        "moderationqueue": "moderation",
                        "bulkoperation": "moderation",
                    }
                    mapped_app_label = model_app_mapping.get(model_type.lower())
                    if mapped_app_label:
                        content_type = ContentType.objects.get_by_natural_key(mapped_app_label, model_type.lower())
                    else:
                        # Fallback to model-only lookup
                        content_type = ContentType.objects.get(model=model_type)
                queryset = queryset.filter(content_type=content_type)
            except ContentType.DoesNotExist:
                pass

        # Filter by object_id (for object-level history)
        object_id = request.query_params.get("object_id")
        if object_id:
            queryset = queryset.filter(object_id=object_id)

        # Filter by user
        user_id = request.query_params.get("user_id")
        if user_id:
            queryset = queryset.filter(by_id=user_id)

        # Filter by date range
        start_date = request.query_params.get("start_date")
        end_date = request.query_params.get("end_date")
        if start_date:
            queryset = queryset.filter(timestamp__gte=start_date)
        if end_date:
            queryset = queryset.filter(timestamp__lte=end_date)

        # Filter by state
        state = request.query_params.get("state")
        if state:
            queryset = queryset.filter(state=state)

        # Search filter (case-insensitive across relevant fields)
        search_query = request.query_params.get("q")
        if search_query:
            queryset = queryset.filter(
                Q(transition__icontains=search_query)
                | Q(description__icontains=search_query)
                | Q(state__icontains=search_query)
                | Q(source_state__icontains=search_query)
                | Q(object_id__icontains=search_query)
                | Q(by__username__icontains=search_query)
            )

        # Order queryset
        queryset = queryset.order_by("-timestamp")

        # Check if HTMX request
        if request.headers.get("HX-Request"):
            # Use Django's Paginator for HTMX responses
            paginator = Paginator(queryset, 20)
            page_number = request.query_params.get("page", 1)
            page_obj = paginator.get_page(page_number)

            return render(
                request,
                "moderation/partials/history_table.html",
                {
                    "history_logs": page_obj,
                    "page_obj": page_obj,
                    "request": request,
                },
            )

        # Paginate for API response
        page = self.paginate_queryset(queryset)
        if page is not None:
            history_data = [
                {
                    "id": log.id,
                    "timestamp": log.timestamp,
                    "model": log.content_type.model,
                    "object_id": log.object_id,
                    "state": log.state,
                    "from_state": log.source_state,
                    "to_state": log.state,
                    "transition": log.transition,
                    "user": log.by.username if log.by else None,
                    "description": log.description,
                    "reason": log.description,
                }
                for log in page
            ]
            return self.get_paginated_response(history_data)

        # Return all history data when pagination is not triggered
        history_data = [
            {
                "id": log.id,
                "timestamp": log.timestamp,
                "model": log.content_type.model,
                "object_id": log.object_id,
                "state": log.state,
                "from_state": log.source_state,
                "to_state": log.state,
                "transition": log.transition,
                "user": log.by.username if log.by else None,
                "description": log.description,
                "reason": log.description,
            }
            for log in queryset
        ]
        return Response(history_data)


# ============================================================================
# Moderation Queue ViewSet
# ============================================================================


class ModerationQueueViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing moderation queue items.

    Provides workflow management for moderation tasks with assignment,
    completion, and progress tracking.
    """

    queryset = ModerationQueue.objects.select_related("assigned_to", "related_report", "content_type").all()

    serializer_class = ModerationQueueSerializer
    permission_classes = [CanViewModerationData]

    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_class = ModerationQueueFilter
    search_fields = ["title", "description"]
    ordering_fields = ["created_at", "updated_at", "priority", "status"]
    ordering = ["-created_at"]

    @action(detail=True, methods=["post"], permission_classes=[IsModeratorOrAdmin])
    def assign(self, request, pk=None):
        """Assign a queue item to a moderator."""
        queue_item = self.get_object()
        serializer = AssignQueueItemSerializer(data=request.data)

        if serializer.is_valid():
            moderator_id = serializer.validated_data["moderator_id"]
            moderator = User.objects.get(id=moderator_id)

            # Check if transition method exists
            transition_method = getattr(queue_item, "transition_to_in_progress", None)
            if transition_method is None:
                return Response(
                    {"error": "Transition method not available"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            # Check if transition can proceed before attempting
            if not can_proceed(transition_method, moderator):
                return Response(
                    format_transition_error(
                        TransitionPermissionDenied(
                            message="Cannot transition to IN_PROGRESS",
                            user_message="You don't have permission to assign this queue item or it cannot be transitioned from the current state.",
                        )
                    ),
                    status=status.HTTP_403_FORBIDDEN,
                )

            queue_item.assigned_to = moderator
            queue_item.assigned_at = timezone.now()
            old_status = queue_item.status
            try:
                transition_method(user=moderator)
            except TransitionPermissionDenied as e:
                return Response(
                    format_transition_error(e),
                    status=status.HTTP_403_FORBIDDEN,
                )
            except TransitionValidationError as e:
                return Response(
                    format_transition_error(e),
                    status=status.HTTP_400_BAD_REQUEST,
                )
            except TransitionNotAllowed as e:
                return Response(
                    format_transition_error(e),
                    status=status.HTTP_400_BAD_REQUEST,
                )

            queue_item.save()

            log_business_event(
                logger,
                event_type="fsm_transition",
                message=f"ModerationQueue {queue_item.id} assigned to {moderator.username}",
                context={
                    "model": "ModerationQueue",
                    "object_id": queue_item.id,
                    "old_state": old_status,
                    "new_state": queue_item.status,
                    "transition": "assign",
                    "moderator": moderator.username,
                },
                request=request,
            )

            response_serializer = self.get_serializer(queue_item)
            return Response(response_serializer.data)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=True, methods=["post"], permission_classes=[IsModeratorOrAdmin])
    def unassign(self, request, pk=None):
        """Unassign a queue item."""
        queue_item = self.get_object()

        # Check if transition method exists
        transition_method = getattr(queue_item, "transition_to_pending", None)
        if transition_method is None:
            return Response(
                {"error": "Transition method not available"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        # Check if transition can proceed before attempting
        if not can_proceed(transition_method, request.user):
            return Response(
                format_transition_error(
                    TransitionPermissionDenied(
                        message="Cannot transition to PENDING",
                        user_message="You don't have permission to unassign this queue item or it cannot be transitioned from the current state.",
                    )
                ),
                status=status.HTTP_403_FORBIDDEN,
            )

        queue_item.assigned_to = None
        queue_item.assigned_at = None
        old_status = queue_item.status
        try:
            transition_method(user=request.user)
        except TransitionPermissionDenied as e:
            return Response(
                format_transition_error(e),
                status=status.HTTP_403_FORBIDDEN,
            )
        except TransitionValidationError as e:
            return Response(
                format_transition_error(e),
                status=status.HTTP_400_BAD_REQUEST,
            )
        except TransitionNotAllowed as e:
            return Response(
                format_transition_error(e),
                status=status.HTTP_400_BAD_REQUEST,
            )

        queue_item.save()

        log_business_event(
            logger,
            event_type="fsm_transition",
            message=f"ModerationQueue {queue_item.id} unassigned",
            context={
                "model": "ModerationQueue",
                "object_id": queue_item.id,
                "old_state": old_status,
                "new_state": queue_item.status,
                "transition": "unassign",
                "user": request.user.username,
            },
            request=request,
        )

        serializer = self.get_serializer(queue_item)
        return Response(serializer.data)

    @action(detail=True, methods=["post"], permission_classes=[IsModeratorOrAdmin])
    def complete(self, request, pk=None):
        """Complete a queue item."""
        queue_item = self.get_object()
        serializer = CompleteQueueItemSerializer(data=request.data)

        if serializer.is_valid():
            action_taken = serializer.validated_data["action"]
            notes = serializer.validated_data.get("notes", "")

            # Check if transition method exists
            transition_method = getattr(queue_item, "transition_to_completed", None)
            if transition_method is None:
                return Response(
                    {"error": "Transition method not available"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            # Check if transition can proceed before attempting
            if not can_proceed(transition_method, request.user):
                return Response(
                    format_transition_error(
                        TransitionPermissionDenied(
                            message="Cannot transition to COMPLETED",
                            user_message="You don't have permission to complete this queue item or it cannot be transitioned from the current state.",
                        )
                    ),
                    status=status.HTTP_403_FORBIDDEN,
                )

            old_status = queue_item.status
            try:
                transition_method(user=request.user)
            except TransitionPermissionDenied as e:
                return Response(
                    format_transition_error(e),
                    status=status.HTTP_403_FORBIDDEN,
                )
            except TransitionValidationError as e:
                return Response(
                    format_transition_error(e),
                    status=status.HTTP_400_BAD_REQUEST,
                )
            except TransitionNotAllowed as e:
                return Response(
                    format_transition_error(e),
                    status=status.HTTP_400_BAD_REQUEST,
                )

            queue_item.save()

            # Create moderation action if needed
            if action_taken != "NO_ACTION" and queue_item.related_report:
                ModerationAction.objects.create(
                    action_type=action_taken,
                    reason=f"Queue item completion: {action_taken}",
                    details=notes,
                    moderator=request.user,
                    target_user=queue_item.related_report.reported_by,
                    related_report=queue_item.related_report,
                    is_active=True,
                )

            log_business_event(
                logger,
                event_type="fsm_transition",
                message=f"ModerationQueue {queue_item.id} completed with action: {action_taken}",
                context={
                    "model": "ModerationQueue",
                    "object_id": queue_item.id,
                    "old_state": old_status,
                    "new_state": queue_item.status,
                    "transition": "complete",
                    "action_taken": action_taken,
                    "user": request.user.username,
                },
                request=request,
            )

            response_serializer = self.get_serializer(queue_item)
            return Response(response_serializer.data)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=False, methods=["get"], permission_classes=[CanViewModerationData])
    def my_queue(self, request):
        """Get queue items assigned to the current user."""
        queryset = self.get_queryset().filter(assigned_to=request.user)

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

    @action(detail=True, methods=["get"], permission_classes=[CanViewModerationData])
    def history(self, request, pk=None):
        """Get transition history for this queue item."""
        from django.contrib.contenttypes.models import ContentType
        from django_fsm_log.models import StateLog

        queue_item = self.get_object()
        content_type = ContentType.objects.get_for_model(queue_item)

        logs = (
            StateLog.objects.filter(content_type=content_type, object_id=queue_item.id)
            .select_related("by")
            .order_by("-timestamp")
        )

        history_data = [
            {
                "id": log.id,
                "timestamp": log.timestamp,
                "state": log.state,
                "from_state": log.source_state,
                "to_state": log.state,
                "transition": log.transition,
                "user": log.by.username if log.by else None,
                "description": log.description,
                "reason": log.description,
            }
            for log in logs
        ]

        return Response(history_data)


# ============================================================================
# Moderation Action ViewSet
# ============================================================================


class ModerationActionViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing moderation actions.

    Tracks actions taken against users and content with expiration
    and status management.
    """

    queryset = ModerationAction.objects.select_related("moderator", "target_user", "related_report").all()

    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_class = ModerationActionFilter
    search_fields = ["reason", "details"]
    ordering_fields = ["created_at", "expires_at", "action_type"]
    ordering = ["-created_at"]

    def get_serializer_class(self):
        """Return appropriate serializer based on action."""
        if self.action == "create":
            return CreateModerationActionSerializer
        return ModerationActionSerializer

    def get_permissions(self):
        """Return appropriate permissions based on action."""
        permission_classes = [IsModeratorOrAdmin] if self.action == "create" else [CanViewModerationData]

        return [permission() for permission in permission_classes]

    @action(detail=True, methods=["post"], permission_classes=[IsModeratorOrAdmin])
    def deactivate(self, request, pk=None):
        """Deactivate a moderation action."""
        action_obj = self.get_object()

        action_obj.is_active = False
        action_obj.save()

        serializer = self.get_serializer(action_obj)
        return Response(serializer.data)

    @action(detail=False, methods=["get"], permission_classes=[CanViewModerationData])
    def active(self, request):
        """Get all active moderation actions."""
        queryset = self.get_queryset().filter(is_active=True, expires_at__gt=timezone.now())

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

    @action(detail=False, methods=["get"], permission_classes=[CanViewModerationData])
    def expired(self, request):
        """Get all expired moderation actions."""
        queryset = self.get_queryset().filter(expires_at__lte=timezone.now(), is_active=True)

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)


# ============================================================================
# Bulk Operation ViewSet
# ============================================================================


class BulkOperationViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing bulk operations.

    Provides administrative bulk operations with progress tracking
    and cancellation support.
    """

    queryset = BulkOperation.objects.select_related("created_by").all()
    permission_classes = [IsAdminOrSuperuser]

    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_class = BulkOperationFilter
    search_fields = ["description"]
    ordering_fields = ["created_at", "started_at", "completed_at", "priority"]
    ordering = ["-created_at"]

    def get_serializer_class(self):
        """Return appropriate serializer based on action."""
        if self.action == "create":
            return CreateBulkOperationSerializer
        return BulkOperationSerializer

    @action(detail=True, methods=["post"])
    def cancel(self, request, pk=None):
        """Cancel a bulk operation."""
        operation = self.get_object()

        if operation.status not in ["PENDING", "RUNNING"]:
            return Response(
                {"error": "Operation cannot be cancelled"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        if not operation.can_cancel:
            return Response(
                {"error": "Operation is not cancellable"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        # Check if transition method exists
        transition_method = getattr(operation, "transition_to_cancelled", None)
        if transition_method is None:
            return Response(
                {"error": "Transition method not available"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        # Check if transition can proceed before attempting
        if not can_proceed(transition_method, request.user):
            return Response(
                format_transition_error(
                    TransitionPermissionDenied(
                        message="Cannot transition to CANCELLED",
                        user_message="You don't have permission to cancel this operation or it cannot be cancelled from the current state.",
                    )
                ),
                status=status.HTTP_403_FORBIDDEN,
            )

        try:
            transition_method(user=request.user)
        except TransitionPermissionDenied as e:
            return Response(
                format_transition_error(e),
                status=status.HTTP_403_FORBIDDEN,
            )
        except TransitionValidationError as e:
            return Response(
                format_transition_error(e),
                status=status.HTTP_400_BAD_REQUEST,
            )
        except TransitionNotAllowed as e:
            return Response(
                format_transition_error(e),
                status=status.HTTP_400_BAD_REQUEST,
            )

        operation.completed_at = timezone.now()
        operation.save()

        serializer = self.get_serializer(operation)
        return Response(serializer.data)

    @action(detail=True, methods=["post"])
    def retry(self, request, pk=None):
        """Retry a failed bulk operation."""
        operation = self.get_object()

        if operation.status != "FAILED":
            return Response(
                {"error": "Only failed operations can be retried"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        # Check if transition method exists
        transition_method = getattr(operation, "transition_to_pending", None)
        if transition_method is None:
            return Response(
                {"error": "Transition method not available"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        # Check if transition can proceed before attempting
        if not can_proceed(transition_method, request.user):
            return Response(
                format_transition_error(
                    TransitionPermissionDenied(
                        message="Cannot transition to PENDING",
                        user_message="You don't have permission to retry this operation or it cannot be retried from the current state.",
                    )
                ),
                status=status.HTTP_403_FORBIDDEN,
            )

        # Reset operation status
        try:
            transition_method(user=request.user)
        except TransitionPermissionDenied as e:
            return Response(
                format_transition_error(e),
                status=status.HTTP_403_FORBIDDEN,
            )
        except TransitionValidationError as e:
            return Response(
                format_transition_error(e),
                status=status.HTTP_400_BAD_REQUEST,
            )
        except TransitionNotAllowed as e:
            return Response(
                format_transition_error(e),
                status=status.HTTP_400_BAD_REQUEST,
            )

        operation.started_at = None
        operation.completed_at = None
        operation.processed_items = 0
        operation.failed_items = 0
        operation.results = {}
        operation.save()

        serializer = self.get_serializer(operation)
        return Response(serializer.data)

    @action(detail=True, methods=["get"])
    def logs(self, request, pk=None):
        """Get logs for a bulk operation."""
        operation = self.get_object()

        # This would typically fetch logs from a logging system
        # For now, return a placeholder response
        logs = {
            "logs": [
                {
                    "timestamp": operation.created_at.isoformat(),
                    "level": "INFO",
                    "message": f"Operation {operation.id} created",
                    "details": operation.parameters,
                }
            ],
            "count": 1,
        }

        return Response(logs)

    @action(detail=False, methods=["get"])
    def running(self, request):
        """Get all running bulk operations."""
        queryset = self.get_queryset().filter(status="RUNNING")

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

    @action(detail=True, methods=["get"])
    def history(self, request, pk=None):
        """Get transition history for this bulk operation."""
        from django.contrib.contenttypes.models import ContentType
        from django_fsm_log.models import StateLog

        operation = self.get_object()
        content_type = ContentType.objects.get_for_model(operation)

        logs = (
            StateLog.objects.filter(content_type=content_type, object_id=operation.id)
            .select_related("by")
            .order_by("-timestamp")
        )

        history_data = [
            {
                "id": log.id,
                "timestamp": log.timestamp,
                "state": log.state,
                "from_state": log.source_state,
                "to_state": log.state,
                "transition": log.transition,
                "user": log.by.username if log.by else None,
                "description": log.description,
                "reason": log.description,
            }
            for log in logs
        ]

        return Response(history_data)


# ============================================================================
# User Moderation ViewSet
# ============================================================================


class UserModerationViewSet(viewsets.ViewSet):
    """
    ViewSet for user moderation operations.

    Provides user-specific moderation data, statistics, and actions.
    """

    permission_classes = [IsModeratorOrAdmin]
    # Default serializer for schema generation
    serializer_class = UserModerationProfileSerializer

    def list(self, request):
        """Search for users to moderate."""
        query = request.query_params.get("q", "")
        if not query:
            return Response([])

        queryset = User.objects.filter(Q(username__icontains=query) | Q(email__icontains=query))[:20]

        users_data = [
            {
                "id": user.id,
                "username": user.username,
                "email": user.email,
                "role": getattr(user, "role", "USER"),
                "is_active": user.is_active,
            }
            for user in queryset
        ]
        return Response(users_data)

    def retrieve(self, request, pk=None):
        """Get moderation profile for a specific user."""
        try:
            user = User.objects.get(pk=pk)
        except User.DoesNotExist:
            return Response({"error": "User not found"}, status=status.HTTP_404_NOT_FOUND)

        # Gather user moderation data
        reports_made = ModerationReport.objects.filter(reported_by=user).count()
        reports_against = ModerationReport.objects.filter(
            reported_entity_type="user", reported_entity_id=user.id
        ).count()

        actions_against = ModerationAction.objects.filter(target_user=user)
        warnings_received = actions_against.filter(action_type="WARNING").count()
        suspensions_received = actions_against.filter(action_type="USER_SUSPENSION").count()
        active_restrictions = actions_against.filter(is_active=True, expires_at__gt=timezone.now()).count()

        # Risk assessment (simplified)
        risk_factors = []
        risk_level = "LOW"

        if reports_against > 5:
            risk_factors.append("Multiple reports against user")
            risk_level = "MEDIUM"

        if suspensions_received > 0:
            risk_factors.append("Previous suspensions")
            risk_level = "HIGH"

        if active_restrictions > 0:
            risk_factors.append("Active restrictions")
            risk_level = "HIGH"

        # Recent activity
        recent_reports = ModerationReport.objects.filter(reported_by=user).order_by("-created_at")[:5]

        recent_actions = actions_against.order_by("-created_at")[:5]

        # Account status
        account_status = "ACTIVE"
        if getattr(user, "is_banned", False):
            account_status = "BANNED"
        elif active_restrictions > 0:
            account_status = "RESTRICTED"

        last_violation = (
            actions_against.filter(action_type__in=["WARNING", "USER_SUSPENSION", "USER_BAN"])
            .order_by("-created_at")
            .first()
        )

        profile_data = {
            "user": {
                "id": user.id,
                "username": user.username,
                "display_name": user.get_display_name(),
                "email": user.email,
                "role": getattr(user, "role", "USER"),
            },
            "reports_made": reports_made,
            "reports_against": reports_against,
            "warnings_received": warnings_received,
            "suspensions_received": suspensions_received,
            "active_restrictions": active_restrictions,
            "risk_level": risk_level,
            "risk_factors": risk_factors,
            "recent_reports": ModerationReportSerializer(recent_reports, many=True).data,
            "recent_actions": ModerationActionSerializer(recent_actions, many=True).data,
            "account_status": account_status,
            "last_violation_date": (last_violation.created_at if last_violation else None),
            "next_review_date": None,  # Would be calculated based on business rules
        }

        return Response(profile_data)

    @action(detail=True, methods=["post"])
    def moderate(self, request, pk=None):
        """Take moderation action against a user."""
        try:
            user = User.objects.get(pk=pk)
        except User.DoesNotExist:
            return Response({"error": "User not found"}, status=status.HTTP_404_NOT_FOUND)

        serializer = CreateModerationActionSerializer(data=request.data, context={"request": request})

        if serializer.is_valid():
            # Override target_user_id with the user from URL
            validated_data = serializer.validated_data.copy()
            validated_data["target_user_id"] = user.id

            action = ModerationAction.objects.create(
                action_type=validated_data["action_type"],
                reason=validated_data["reason"],
                details=validated_data["details"],
                duration_hours=validated_data.get("duration_hours"),
                moderator=request.user,
                target_user=user,
                related_report_id=validated_data.get("related_report_id"),
                is_active=True,
                expires_at=(
                    timezone.now() + timedelta(hours=validated_data["duration_hours"])
                    if validated_data.get("duration_hours")
                    else None
                ),
            )

            response_serializer = ModerationActionSerializer(action)
            return Response(response_serializer.data, status=status.HTTP_201_CREATED)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=False, methods=["get"])
    def search(self, request):
        """Search users for moderation purposes."""
        query = request.query_params.get("query", "")
        role = request.query_params.get("role")
        has_restrictions = request.query_params.get("has_restrictions")

        queryset = User.objects.all()

        if query:
            queryset = queryset.filter(Q(username__icontains=query) | Q(email__icontains=query))

        if role:
            queryset = queryset.filter(role=role)

        if has_restrictions == "true":
            active_action_users = ModerationAction.objects.filter(
                is_active=True, expires_at__gt=timezone.now()
            ).values_list("target_user_id", flat=True)
            queryset = queryset.filter(id__in=active_action_users)

        # Paginate results
        page = self.paginate_queryset(queryset)
        if page is not None:
            users_data = []
            for user in page:
                restriction_count = ModerationAction.objects.filter(
                    target_user=user, is_active=True, expires_at__gt=timezone.now()
                ).count()

                users_data.append(
                    {
                        "id": user.id,
                        "username": user.username,
                        "display_name": user.get_display_name(),
                        "email": user.email,
                        "role": getattr(user, "role", "USER"),
                        "date_joined": user.date_joined,
                        "last_login": user.last_login,
                        "is_active": user.is_active,
                        "restriction_count": restriction_count,
                        "risk_level": "HIGH" if restriction_count > 0 else "LOW",
                    }
                )

            return self.get_paginated_response(users_data)

        return Response([])

    @action(detail=False, methods=["get"])
    def stats(self, request):
        """Get overall user moderation statistics."""
        total_actions = ModerationAction.objects.count()
        active_actions = ModerationAction.objects.filter(is_active=True, expires_at__gt=timezone.now()).count()
        expired_actions = ModerationAction.objects.filter(expires_at__lte=timezone.now()).count()

        stats_data = {
            "total_actions": total_actions,
            "active_actions": active_actions,
            "expired_actions": expired_actions,
        }

        return Response(stats_data)


# ============================================================================
# Submission ViewSets
# ============================================================================


class EditSubmissionViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing edit submissions.

    Includes claim/unclaim endpoints with concurrency protection using
    database row locking (select_for_update) to prevent race conditions.
    """

    queryset = EditSubmission.objects.all()
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    search_fields = ["reason", "changes"]
    ordering_fields = ["created_at", "status"]
    ordering = ["-created_at"]
    permission_classes = [CanViewModerationData]

    def get_serializer_class(self):
        if self.action == "list":
            return EditSubmissionListSerializer
        if self.action == "create":
            return CreateEditSubmissionSerializer
        return EditSubmissionSerializer

    def get_queryset(self):
        queryset = super().get_queryset()
        status = self.request.query_params.get("status")
        if status:
            queryset = queryset.filter(status=status)

        # User filter
        user_id = self.request.query_params.get("user")
        if user_id:
            queryset = queryset.filter(user_id=user_id)

        return queryset

    @action(detail=False, methods=["post"], permission_classes=[CanViewModerationData], url_path="with-diffs")
    def with_diffs(self, request):
        """
        Fetch submission items with pre-calculated diffs.
        
        POST /api/v1/moderation/api/submissions/with-diffs/
        
        Request body:
            submission_id: str - The EditSubmission ID to fetch
        
        Returns:
            items: list - List of submission items with diffs calculated
        """
        from deepdiff import DeepDiff
        
        submission_id = request.data.get("submission_id")
        
        if not submission_id:
            return Response(
                {"error": "submission_id is required"},
                status=status.HTTP_400_BAD_REQUEST,
            )
        
        try:
            submission = EditSubmission.objects.get(pk=submission_id)
        except EditSubmission.DoesNotExist:
            return Response(
                {"error": "Submission not found"},
                status=status.HTTP_404_NOT_FOUND,
            )
        except Exception:
            return Response(
                {"error": "Invalid submission_id format"},
                status=status.HTTP_400_BAD_REQUEST,
            )
        
        # Get submission changes
        entity_data = submission.changes or {}
        original_data = None
        
        # Get entity type from content_type
        entity_type = submission.content_type.model if submission.content_type else None
        
        # If this is an EDIT submission, try to get the original entity data
        if submission.object_id and entity_type:
            try:
                model_class = submission.content_type.model_class()
                if model_class:
                    original_entity = model_class.objects.get(pk=submission.object_id)
                    
                    from django.forms.models import model_to_dict
                    original_data = model_to_dict(original_entity)
            except Exception as e:
                logger.debug(f"Could not fetch original entity for diff: {e}")
        
        # Calculate field-level diffs
        field_changes = []
        
        if original_data and entity_data:
            # Check if entity_data already contains pre-computed diff objects {new, old}
            # This happens when the changes dict stores diffs directly
            has_precomputed_diffs = any(
                isinstance(value, dict) and "new" in value and "old" in value and len(value) == 2
                for value in entity_data.values()
                if isinstance(value, dict)
            )
            
            if has_precomputed_diffs:
                # Extract field changes directly from pre-computed diffs
                for field, value in entity_data.items():
                    if field.startswith("_"):
                        continue
                    
                    if (
                        isinstance(value, dict) 
                        and "new" in value 
                        and "old" in value 
                        and len(value) == 2
                    ):
                        field_changes.append({
                            "field": field,
                            "oldValue": value.get("old"),
                            "newValue": value.get("new"),
                            "changeType": "modified",
                            "category": "other",
                            "priority": "optional",
                        })
            else:
                # Use DeepDiff for regular data comparison
                try:
                    diff = DeepDiff(original_data, entity_data, ignore_order=True)
                    
                    for change_type, changes in diff.items():
                        if isinstance(changes, dict):
                            for field_path, change_value in changes.items():
                                field_name = field_path.replace("root['", "").replace("']", "").split("']['")[0]
                                
                                if change_type == "values_changed":
                                    field_changes.append({
                                        "field": field_name,
                                        "oldValue": change_value.get("old_value"),
                                        "newValue": change_value.get("new_value"),
                                        "changeType": "modified",
                                        "category": "other",
                                        "priority": "optional",
                                    })
                                elif change_type == "dictionary_item_added":
                                    field_changes.append({
                                        "field": field_name,
                                        "oldValue": None,
                                        "newValue": change_value,
                                        "changeType": "added",
                                        "category": "other",
                                        "priority": "optional",
                                    })
                                elif change_type == "dictionary_item_removed":
                                    field_changes.append({
                                        "field": field_name,
                                        "oldValue": change_value,
                                        "newValue": None,
                                        "changeType": "removed",
                                        "category": "other",
                                        "priority": "optional",
                                    })
                except Exception as e:
                    logger.debug(f"Error calculating diffs: {e}")
        elif entity_data:
            # Handle entity_data that may contain pre-computed diff objects {new, old}
            for field, value in entity_data.items():
                if field.startswith("_"):
                    continue
                    
                # Check if value is a diff object with {new, old} structure
                if (
                    isinstance(value, dict) 
                    and "new" in value 
                    and "old" in value 
                    and len(value) == 2
                ):
                    # This is a pre-computed diff, extract the values
                    field_changes.append({
                        "field": field,
                        "oldValue": value.get("old"),
                        "newValue": value.get("new"),
                        "changeType": "modified",
                        "category": "other",
                        "priority": "optional",
                    })
                else:
                    # Regular value (for create submissions)
                    field_changes.append({
                        "field": field,
                        "oldValue": None,
                        "newValue": value,
                        "changeType": "added",
                        "category": "other",
                        "priority": "optional",
                    })
        
        action_type = "edit" if submission.object_id else "create"
        
        item = {
            "id": str(submission.id),
            "submission_id": str(submission.id),
            "item_type": entity_type or "unknown",
            "action_type": action_type,
            "status": submission.status,
            "order_index": 0,
            "depends_on": None,
            "entity_data": entity_data,
            "original_entity_data": original_data,
            "item_data": entity_data,
            "original_data": original_data,
            "diff": {
                "action": action_type,
                "fieldChanges": field_changes,
                "unchangedFields": [],
                "totalChanges": len(field_changes),
            },
            "created_at": submission.created_at.isoformat() if submission.created_at else None,
            "updated_at": submission.updated_at.isoformat() if hasattr(submission, "updated_at") and submission.updated_at else None,
        }
        
        return Response({"items": [item]})

    @action(detail=True, methods=["post"], permission_classes=[IsModeratorOrAdmin])
    def claim(self, request, pk=None):
        """
        Claim a submission for review with concurrency protection.

        Uses select_for_update() to acquire a database row lock,
        preventing race conditions when multiple moderators try to
        claim the same submission simultaneously.

        Returns:
            200: Submission successfully claimed
            404: Submission not found
            409: Submission already claimed or being claimed by another moderator
            400: Invalid state for claiming
        """
        from django.core.exceptions import ValidationError
        from django.db import DatabaseError, transaction

        with transaction.atomic():
            try:
                # Lock the row for update - other transactions will fail immediately
                submission = EditSubmission.objects.select_for_update(nowait=True).get(pk=pk)
            except EditSubmission.DoesNotExist:
                return Response({"error": "Submission not found"}, status=status.HTTP_404_NOT_FOUND)
            except DatabaseError:
                # Row is already locked by another transaction
                return Response(
                    {"error": "Submission is being claimed by another moderator. Please try again."},
                    status=status.HTTP_409_CONFLICT,
                )

            # Check if already claimed
            if submission.status == "CLAIMED":
                return Response(
                    {
                        "error": "Submission already claimed",
                        "claimed_by": submission.claimed_by.username if submission.claimed_by else None,
                        "claimed_at": submission.claimed_at.isoformat() if submission.claimed_at else None,
                    },
                    status=status.HTTP_409_CONFLICT,
                )

            # Check if in valid state for claiming
            if submission.status != "PENDING":
                return Response(
                    {"error": f"Cannot claim submission in {submission.status} state"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            try:
                submission.claim(user=request.user)
                log_business_event(
                    logger,
                    event_type="submission_claimed",
                    message=f"EditSubmission {submission.id} claimed by {request.user.username}",
                    context={
                        "model": "EditSubmission",
                        "object_id": submission.id,
                        "claimed_by": request.user.username,
                    },
                    request=request,
                )
                # Return response in format expected by frontend useModerationQueue.ts
                # Frontend expects: { locked_until: "...", submission_id: "..." } at top level
                lock_duration_minutes = 15
                locked_until = submission.claimed_at + timedelta(minutes=lock_duration_minutes)
                return Response({
                    "success": True,
                    "locked_until": locked_until.isoformat(),
                    "lockedUntil": locked_until.isoformat(),  # Both camelCase and snake_case for compatibility
                    "submission_id": str(submission.id),
                    "submissionId": str(submission.id),
                    "claimed_by": request.user.username,
                    "claimed_at": submission.claimed_at.isoformat() if submission.claimed_at else None,
                    "status": submission.status,
                    "lock_duration_minutes": lock_duration_minutes,
                })
            except ValidationError as e:
                return Response({"success": False, "error": str(e)}, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=True, methods=["post"], permission_classes=[IsModeratorOrAdmin])
    def unclaim(self, request, pk=None):
        """
        Release claim on a submission.

        Only the claiming moderator or an admin can unclaim a submission.
        """
        from django.core.exceptions import ValidationError

        submission = self.get_object()

        # Only the claiming user or an admin can unclaim
        if submission.claimed_by != request.user and not request.user.is_staff:
            return Response(
                {"error": "Only the claiming moderator or an admin can unclaim"}, status=status.HTTP_403_FORBIDDEN
            )

        if submission.status != "CLAIMED":
            return Response({"error": "Submission is not claimed"}, status=status.HTTP_400_BAD_REQUEST)

        try:
            submission.unclaim(user=request.user)
            log_business_event(
                logger,
                event_type="submission_unclaimed",
                message=f"EditSubmission {submission.id} unclaimed by {request.user.username}",
                context={
                    "model": "EditSubmission",
                    "object_id": submission.id,
                    "unclaimed_by": request.user.username,
                },
                request=request,
            )
            return Response(self.get_serializer(submission).data)
        except ValidationError as e:
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=True, methods=["post"], permission_classes=[IsModeratorOrAdmin])
    def approve(self, request, pk=None):
        submission = self.get_object()
        user = request.user

        try:
            submission.approve(moderator=user)
            return Response(self.get_serializer(submission).data)
        except Exception as e:
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=True, methods=["post"], permission_classes=[IsModeratorOrAdmin])
    def reject(self, request, pk=None):
        submission = self.get_object()
        user = request.user
        reason = request.data.get("reason", "")

        try:
            submission.reject(moderator=user, reason=reason)
            return Response(self.get_serializer(submission).data)
        except Exception as e:
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=True, methods=["post"], permission_classes=[IsModeratorOrAdmin])
    def escalate(self, request, pk=None):
        submission = self.get_object()
        user = request.user
        reason = request.data.get("reason", "")

        try:
            submission.escalate(moderator=user, reason=reason)
            return Response(self.get_serializer(submission).data)
        except Exception as e:
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=False, methods=["post"], permission_classes=[IsModeratorOrAdmin], url_path="release-expired")
    def release_expired_locks(self, request):
        """
        Release all expired claim locks.
        
        This is typically handled by a Celery task, but can be triggered manually.
        Claims are expired after 30 minutes by default.
        """
        from datetime import timedelta
        
        expiry_threshold = timezone.now() - timedelta(minutes=30)
        
        expired_claims = EditSubmission.objects.filter(
            status="CLAIMED",
            claimed_at__lt=expiry_threshold
        )
        
        released_count = 0
        for submission in expired_claims:
            submission.status = "PENDING"
            submission.claimed_by = None
            submission.claimed_at = None
            submission.save(update_fields=["status", "claimed_by", "claimed_at"])
            released_count += 1
        
        return Response({
            "released_count": released_count,
            "message": f"Released {released_count} expired lock(s)"
        })

    @action(detail=True, methods=["post"], permission_classes=[IsAdminOrSuperuser], url_path="admin-release")
    def admin_release(self, request, pk=None):
        """
        Admin/superuser force release of a specific claim.
        """
        submission = self.get_object()
        
        if submission.status != "CLAIMED":
            return Response(
                {"error": "Submission is not claimed"},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        submission.status = "PENDING"
        submission.claimed_by = None
        submission.claimed_at = None
        submission.save(update_fields=["status", "claimed_by", "claimed_at"])
        
        return Response({
            "success": True,
            "message": f"Lock released on submission {submission.id}"
        })

    @action(detail=False, methods=["post"], permission_classes=[IsAdminOrSuperuser], url_path="admin-release-all")
    def admin_release_all(self, request):
        """
        Admin/superuser force release of all active claims.
        """
        claimed_submissions = EditSubmission.objects.filter(status="CLAIMED")
        
        released_count = 0
        for submission in claimed_submissions:
            submission.status = "PENDING"
            submission.claimed_by = None
            submission.claimed_at = None
            submission.save(update_fields=["status", "claimed_by", "claimed_at"])
            released_count += 1
        
        return Response({
            "released_count": released_count,
            "message": f"Released all {released_count} active lock(s)"
        })

    @action(detail=True, methods=["post"], permission_classes=[IsModeratorOrAdmin], url_path="reassign")
    def reassign(self, request, pk=None):
        """
        Reassign a submission to a different moderator.
        
        Only admins can reassign submissions claimed by other moderators.
        The submission must be in CLAIMED status.
        """
        submission = self.get_object()
        new_moderator_id = request.data.get("new_moderator_id")
        
        if not new_moderator_id:
            return Response(
                {"error": "new_moderator_id is required"},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        try:
            new_moderator = User.objects.get(pk=new_moderator_id)
        except User.DoesNotExist:
            return Response(
                {"error": "Moderator not found"},
                status=status.HTTP_404_NOT_FOUND
            )
        
        # Check moderator permissions
        if new_moderator.role not in ["MODERATOR", "ADMIN", "SUPERUSER"]:
            return Response(
                {"error": "User is not a moderator"},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        # Update the claim
        submission.claimed_by = new_moderator
        submission.claimed_at = timezone.now()
        submission.save(update_fields=["claimed_by", "claimed_at"])
        
        return Response({
            "success": True,
            "message": f"Submission reassigned to {new_moderator.username}"
        })

    @action(detail=False, methods=["post"], permission_classes=[IsModeratorOrAdmin], url_path="audit-log")
    def log_admin_action(self, request):
        """
        Log an admin action for audit trail.
        
        This creates an audit log entry for moderator actions.
        """
        action_type = request.data.get("action_type", "")
        action_details = request.data.get("action_details", {})
        target_entity = request.data.get("target_entity", {})
        
        # Create audit log entry
        logger.info(
            f"[AdminAction] User {request.user.username} - {action_type}",
            extra={
                "user_id": request.user.id,
                "action_type": action_type,
                "action_details": action_details,
                "target_entity": target_entity,
            }
        )
        
        return Response({
            "success": True,
            "message": "Action logged successfully"
        })

    @action(detail=False, methods=["get"], permission_classes=[IsModeratorOrAdmin], url_path="my-active-claim")
    def my_active_claim(self, request):
        """
        Get the current user's active claim on any submission.
        
        Used by lock restoration to restore a moderator's active claim after
        page refresh. Returns the most recent CLAIMED submission for this user.
        
        Returns:
            200: Active claim found with submission data
            200: No active claim (empty data)
        """
        user = request.user
        
        # Find any submission claimed by this user
        claimed_submission = (
            EditSubmission.objects.filter(
                claimed_by=user,
                status="CLAIMED"
            )
            .order_by("-claimed_at")
            .first()
        )
        
        if not claimed_submission:
            return Response({
                "active_claim": None,
                "message": "No active claims found"
            })
        
        return Response({
            "active_claim": {
                "id": claimed_submission.id,
                "status": claimed_submission.status,
                "claimed_at": claimed_submission.claimed_at.isoformat() if claimed_submission.claimed_at else None,
                # Include basic submission info for context
                "content_type": claimed_submission.content_type.model if claimed_submission.content_type else None,
                "object_id": claimed_submission.object_id,
            },
            "message": "Active claim found"
        })

    @action(detail=True, methods=["post"], permission_classes=[IsModeratorOrAdmin])
    def extend(self, request, pk=None):
        """
        Extend the lock on a claimed submission.

        Only the claiming moderator can extend the lock.
        Extends the lock by the default duration (15 minutes).

        Returns:
            200: Lock extended with new expiration time
            400: Submission not in claimed state
            403: User is not the claiming moderator
            404: Submission not found
        """
        submission = self.get_object()
        user = request.user

        # Only the claiming user can extend
        if submission.claimed_by != user:
            return Response(
                {"error": "Only the claiming moderator can extend the lock"},
                status=status.HTTP_403_FORBIDDEN,
            )

        if submission.status != "CLAIMED":
            return Response(
                {"error": "Submission is not claimed"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        # Extend the claim time by 15 minutes
        extension_minutes = request.data.get("extension_minutes", 15)
        new_claimed_at = timezone.now()
        submission.claimed_at = new_claimed_at
        submission.save(update_fields=["claimed_at"])

        new_expires_at = new_claimed_at + timedelta(minutes=extension_minutes)

        log_business_event(
            logger,
            event_type="submission_lock_extended",
            message=f"EditSubmission {submission.id} lock extended by {user.username}",
            context={
                "model": "EditSubmission",
                "object_id": submission.id,
                "extended_by": user.username,
                "new_expires_at": new_expires_at.isoformat(),
            },
            request=request,
        )

        return Response({
            "success": True,
            "new_expiry": new_expires_at.isoformat(),
            "newExpiresAt": new_expires_at.isoformat(),  # CamelCase for compatibility
            "submission_id": str(submission.id),
            "extension_minutes": extension_minutes,
        })

    @action(detail=True, methods=["post"], permission_classes=[IsModeratorOrAdmin])
    def release(self, request, pk=None):
        """
        Release the lock on a claimed submission (alias for unclaim).

        This is a convenience endpoint that mirrors the unclaim behavior
        but is named to match the frontend's lock terminology.

        Returns:
            200: Lock released successfully
            400: Submission not in claimed state
            403: User is not the claiming moderator or admin
            404: Submission not found
        """
        from django.core.exceptions import ValidationError

        submission = self.get_object()
        user = request.user
        silent = request.data.get("silent", False)

        # Only the claiming user or an admin can release
        if submission.claimed_by != user and not user.is_staff:
            return Response(
                {"error": "Only the claiming moderator or an admin can release the lock"},
                status=status.HTTP_403_FORBIDDEN,
            )

        if submission.status != "CLAIMED":
            return Response(
                {"error": "Submission is not claimed"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        try:
            submission.unclaim(user=user)
            log_business_event(
                logger,
                event_type="submission_lock_released",
                message=f"EditSubmission {submission.id} lock released by {user.username}",
                context={
                    "model": "EditSubmission",
                    "object_id": submission.id,
                    "released_by": user.username,
                    "silent": silent,
                },
                request=request,
            )
            return Response({
                "success": True,
                "message": "Lock released successfully",
                "submission_id": str(submission.id),
            })
        except ValidationError as e:
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=True, methods=["post"], permission_classes=[IsModeratorOrAdmin], url_path="convert-to-edit")
    def convert_to_edit(self, request, pk=None):
        """
        Convert a pending entity submission to an edit suggestion.

        This is used when a new entity submission should be merged with
        an existing entity rather than creating a new one.

        Request body:
            target_entity_type: str - The type of entity to merge into (e.g., 'park', 'ride')
            target_entity_id: int - The ID of the existing entity
            merge_fields: list[str] - Optional list of fields to merge (defaults to all)
            notes: str - Optional moderator notes

        Returns:
            200: Submission successfully converted
            400: Invalid request or conversion not possible
            404: Submission or target entity not found
        """
        from django.contrib.contenttypes.models import ContentType

        submission = self.get_object()
        user = request.user

        # Validate submission state
        if submission.status not in ["PENDING", "CLAIMED"]:
            return Response(
                {"error": f"Cannot convert submission in {submission.status} state"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        # Get request data
        target_entity_type = request.data.get("target_entity_type")
        target_entity_id = request.data.get("target_entity_id")
        merge_fields = request.data.get("merge_fields", [])
        notes = request.data.get("notes", "")

        if not target_entity_type or not target_entity_id:
            return Response(
                {"error": "target_entity_type and target_entity_id are required"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        # Look up the target entity
        try:
            app_label = "parks" if target_entity_type in ["park"] else "rides"
            if target_entity_type == "company":
                app_label = "core"

            content_type = ContentType.objects.get(app_label=app_label, model=target_entity_type)
            model_class = content_type.model_class()
            target_entity = model_class.objects.get(pk=target_entity_id)
        except (ContentType.DoesNotExist, Exception) as e:
            return Response(
                {"error": f"Target entity not found: {target_entity_type}#{target_entity_id}"},
                status=status.HTTP_404_NOT_FOUND,
            )

        # Store the conversion metadata
        conversion_data = {
            "converted_from": "new_entity_submission",
            "target_entity_type": target_entity_type,
            "target_entity_id": target_entity_id,
            "target_entity_name": str(target_entity),
            "merge_fields": merge_fields,
            "converted_by": user.username,
            "converted_at": timezone.now().isoformat(),
            "notes": notes,
        }

        # Update the submission
        if hasattr(submission, "changes") and isinstance(submission.changes, dict):
            submission.changes["_conversion_metadata"] = conversion_data
        else:
            # Create changes dict if it doesn't exist
            submission.changes = {"_conversion_metadata": conversion_data}

        # Add moderator note
        if hasattr(submission, "moderator_notes"):
            existing_notes = submission.moderator_notes or ""
            submission.moderator_notes = (
                f"{existing_notes}\n\n[Converted to edit] {notes}".strip()
                if notes
                else f"{existing_notes}\n\n[Converted to edit for {target_entity_type} #{target_entity_id}]".strip()
            )

        submission.save()

        # Log the conversion
        log_business_event(
            logger,
            event_type="submission_converted_to_edit",
            message=f"EditSubmission {submission.id} converted to edit for {target_entity_type}#{target_entity_id}",
            context={
                "model": "EditSubmission",
                "object_id": submission.id,
                "target_entity_type": target_entity_type,
                "target_entity_id": target_entity_id,
                "converted_by": user.username,
            },
            request=request,
        )

        return Response({
            "success": True,
            "message": f"Submission converted to edit for {target_entity_type} #{target_entity_id}",
            "submission": self.get_serializer(submission).data,
            "conversion_metadata": conversion_data,
        })


class PhotoSubmissionViewSet(viewsets.ModelViewSet):
    """
    ViewSet for managing photo submissions.

    Includes claim/unclaim endpoints with concurrency protection using
    database row locking (select_for_update) to prevent race conditions.
    """

    queryset = PhotoSubmission.objects.all()
    serializer_class = PhotoSubmissionSerializer
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    search_fields = ["caption", "notes"]
    ordering_fields = ["created_at", "status"]
    ordering = ["-created_at"]
    permission_classes = [CanViewModerationData]

    def get_queryset(self):
        queryset = super().get_queryset()
        status = self.request.query_params.get("status")
        if status:
            queryset = queryset.filter(status=status)

        # User filter
        user_id = self.request.query_params.get("user")
        if user_id:
            queryset = queryset.filter(user_id=user_id)

        return queryset

    @action(detail=True, methods=["post"], permission_classes=[IsModeratorOrAdmin])
    def claim(self, request, pk=None):
        """
        Claim a photo submission for review with concurrency protection.

        Uses select_for_update() to acquire a database row lock.
        """
        from django.core.exceptions import ValidationError
        from django.db import DatabaseError, transaction

        with transaction.atomic():
            try:
                submission = PhotoSubmission.objects.select_for_update(nowait=True).get(pk=pk)
            except PhotoSubmission.DoesNotExist:
                return Response({"error": "Submission not found"}, status=status.HTTP_404_NOT_FOUND)
            except DatabaseError:
                return Response(
                    {"error": "Submission is being claimed by another moderator. Please try again."},
                    status=status.HTTP_409_CONFLICT,
                )

            if submission.status == "CLAIMED":
                return Response(
                    {
                        "error": "Submission already claimed",
                        "claimed_by": submission.claimed_by.username if submission.claimed_by else None,
                        "claimed_at": submission.claimed_at.isoformat() if submission.claimed_at else None,
                    },
                    status=status.HTTP_409_CONFLICT,
                )

            if submission.status != "PENDING":
                return Response(
                    {"error": f"Cannot claim submission in {submission.status} state"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            try:
                submission.claim(user=request.user)
                log_business_event(
                    logger,
                    event_type="submission_claimed",
                    message=f"PhotoSubmission {submission.id} claimed by {request.user.username}",
                    context={
                        "model": "PhotoSubmission",
                        "object_id": submission.id,
                        "claimed_by": request.user.username,
                    },
                    request=request,
                )
                # Return response in format expected by frontend useModerationQueue.ts
                # Frontend expects: { locked_until: "...", submission_id: "..." } at top level
                lock_duration_minutes = 15
                locked_until = submission.claimed_at + timedelta(minutes=lock_duration_minutes)
                return Response({
                    "success": True,
                    "locked_until": locked_until.isoformat(),
                    "lockedUntil": locked_until.isoformat(),  # Both camelCase and snake_case for compatibility
                    "submission_id": str(submission.id),
                    "submissionId": str(submission.id),
                    "claimed_by": request.user.username,
                    "claimed_at": submission.claimed_at.isoformat() if submission.claimed_at else None,
                    "status": submission.status,
                    "lock_duration_minutes": lock_duration_minutes,
                })
            except ValidationError as e:
                return Response({"success": False, "error": str(e)}, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=True, methods=["post"], permission_classes=[IsModeratorOrAdmin])
    def unclaim(self, request, pk=None):
        """Release claim on a photo submission."""
        from django.core.exceptions import ValidationError

        submission = self.get_object()

        if submission.claimed_by != request.user and not request.user.is_staff:
            return Response(
                {"error": "Only the claiming moderator or an admin can unclaim"}, status=status.HTTP_403_FORBIDDEN
            )

        if submission.status != "CLAIMED":
            return Response({"error": "Submission is not claimed"}, status=status.HTTP_400_BAD_REQUEST)

        try:
            submission.unclaim(user=request.user)
            log_business_event(
                logger,
                event_type="submission_unclaimed",
                message=f"PhotoSubmission {submission.id} unclaimed by {request.user.username}",
                context={
                    "model": "PhotoSubmission",
                    "object_id": submission.id,
                    "unclaimed_by": request.user.username,
                },
                request=request,
            )
            return Response(self.get_serializer(submission).data)
        except ValidationError as e:
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=True, methods=["post"], permission_classes=[IsModeratorOrAdmin])
    def approve(self, request, pk=None):
        submission = self.get_object()
        user = request.user
        notes = request.data.get("notes", "")

        try:
            submission.approve(moderator=user, notes=notes)
            return Response(self.get_serializer(submission).data)
        except Exception as e:
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=True, methods=["post"], permission_classes=[IsModeratorOrAdmin])
    def reject(self, request, pk=None):
        submission = self.get_object()
        user = request.user
        notes = request.data.get("notes", "")

        try:
            submission.reject(moderator=user, notes=notes)
            return Response(self.get_serializer(submission).data)
        except Exception as e:
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=True, methods=["post"], permission_classes=[IsModeratorOrAdmin])
    def escalate(self, request, pk=None):
        submission = self.get_object()
        user = request.user
        notes = request.data.get("notes", "")

        try:
            submission.escalate(moderator=user, notes=notes)
            return Response(self.get_serializer(submission).data)
        except Exception as e:
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)


# ============================================================================
# Standalone Convert Submission to Edit View
# ============================================================================


from rest_framework.views import APIView


class ConvertSubmissionToEditView(APIView):
    """
    POST /api/moderation/api/convert-to-edit/
    
    Convert a CREATE submission to an EDIT by linking it to an existing entity.
    
    Full parity with Supabase Edge Function: convert-submission-to-edit
    
    This endpoint:
    1. Validates the submission is locked by the requesting moderator
    2. Validates the submission is in a valid state (PENDING or CLAIMED)  
    3. Validates the submission_type is 'CREATE' (only CREATE can be converted)
    4. Looks up the existing entity
    5. Updates the submission_type to 'EDIT' and links to existing entity
    6. Logs to audit trail
    
    BULLETPROOFED: Transaction safety, UUID validation, comprehensive error handling.
    
    Request body:
    {
        "submissionId": "...",      # The EditSubmission ID
        "itemId": "...",            # The submission item ID (optional, for Supabase compat)
        "existingEntityId": "...",  # The existing entity to link to
        "conversionType": "..."     # Optional: 'automatic' or 'manual'
    }
    
    Returns:
    {
        "success": true/false,
        "itemId": "...",
        "submissionId": "...",
        "existingEntityId": "...",
        "existingEntityName": "...",
        "message": "..."
    }
    """
    
    permission_classes = [IsModeratorOrAdmin]
    
    # Validation constants
    MAX_NOTE_LENGTH = 5000
    ALLOWED_CONVERSION_TYPES = {"automatic", "manual", "duplicate_detected"}
    VALID_STATES = {"PENDING", "CLAIMED", "pending", "partially_approved", "claimed"}
    
    def post(self, request):
        from django.db import transaction
        from django.contrib.contenttypes.models import ContentType
        import uuid
        
        try:
            # ================================================================
            # STEP 0: Validate user is authenticated
            # ================================================================
            user = request.user
            if not user or not user.is_authenticated:
                return Response(
                    {"success": False, "message": "Authentication required"},
                    status=status.HTTP_401_UNAUTHORIZED,
                )
            
            # ================================================================
            # STEP 1: Extract and validate request parameters
            # ================================================================
            submission_id = request.data.get("submissionId")
            item_id = request.data.get("itemId")  # For Supabase compatibility
            existing_entity_id = request.data.get("existingEntityId")
            conversion_type = request.data.get("conversionType", "automatic")
            
            # Validate required parameters
            if not submission_id:
                return Response(
                    {"success": False, "message": "submissionId is required"},
                    status=status.HTTP_400_BAD_REQUEST,
                )
            
            if not existing_entity_id:
                return Response(
                    {"success": False, "message": "existingEntityId is required"},
                    status=status.HTTP_400_BAD_REQUEST,
                )
            
            # Validate UUID formats
            try:
                if isinstance(submission_id, str):
                    submission_uuid = uuid.UUID(submission_id)
                else:
                    submission_uuid = submission_id
            except (ValueError, AttributeError):
                return Response(
                    {"success": False, "message": "Invalid submissionId format"},
                    status=status.HTTP_400_BAD_REQUEST,
                )
            
            try:
                if isinstance(existing_entity_id, str):
                    entity_uuid = uuid.UUID(existing_entity_id)
                else:
                    entity_uuid = existing_entity_id
            except (ValueError, AttributeError):
                return Response(
                    {"success": False, "message": "Invalid existingEntityId format"},
                    status=status.HTTP_400_BAD_REQUEST,
                )
            
            # Sanitize conversion_type
            if not isinstance(conversion_type, str):
                conversion_type = "automatic"
            conversion_type = conversion_type.strip().lower()[:50]
            if conversion_type not in self.ALLOWED_CONVERSION_TYPES:
                conversion_type = "automatic"
            
            # ================================================================
            # STEP 2: Get the submission with select_for_update
            # ================================================================
            try:
                with transaction.atomic():
                    submission = EditSubmission.objects.select_for_update().get(pk=submission_uuid)
            except EditSubmission.DoesNotExist:
                return Response(
                    {"success": False, "message": "Submission not found"},
                    status=status.HTTP_404_NOT_FOUND,
                )
            except Exception as e:
                logger.warning(f"Failed to fetch submission {submission_id}: {e}")
                return Response(
                    {"success": False, "message": "Failed to fetch submission"},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                )
            
            # ================================================================
            # STEP 3: Verify submission is locked by requesting moderator
            # ================================================================
            claimed_by_id = getattr(submission, 'claimed_by_id', None)
            user_id = getattr(user, 'id', None)
            
            if claimed_by_id != user_id:
                # Additional check: allow admins to override
                if not getattr(user, 'is_staff', False) and not getattr(user, 'is_superuser', False):
                    return Response(
                        {"success": False, "message": "You must claim this submission before converting it"},
                        status=status.HTTP_400_BAD_REQUEST,
                    )
                logger.info(
                    f"Admin override: {user.username} converting submission claimed by user {claimed_by_id}",
                    extra={"submission_id": str(submission_uuid), "admin_user": user.username}
                )
            
            # ================================================================
            # STEP 4: Validate submission state
            # ================================================================
            current_status = getattr(submission, 'status', 'unknown')
            if current_status not in self.VALID_STATES:
                return Response(
                    {"success": False, "message": f"Submission must be pending or claimed to convert (current: {current_status})"},
                    status=status.HTTP_400_BAD_REQUEST,
                )
            
            # ================================================================
            # STEP 5: Validate submission_type is CREATE
            # ================================================================
            current_type = getattr(submission, 'submission_type', '')
            if current_type != "CREATE":
                return Response(
                    {
                        "success": False, 
                        "message": f"Item is already set to '{current_type}', cannot convert"
                    },
                    status=status.HTTP_400_BAD_REQUEST,
                )
            
            # ================================================================
            # STEP 6: Determine entity type from submission's content_type
            # ================================================================
            target_entity_type = None
            target_entity_name = None
            target_entity_slug = None
            target_entity = None
            
            if submission.content_type:
                target_entity_type = submission.content_type.model
            
            # Also try to get from changes if available
            if not target_entity_type and isinstance(submission.changes, dict):
                target_entity_type = submission.changes.get("entity_type")
            
            # ================================================================
            # STEP 7: Look up the existing entity
            # ================================================================
            app_label_map = {
                "park": "parks",
                "ride": "rides", 
                "company": "core",
                "ridemodel": "rides",
                "manufacturer": "core",
                "operator": "core",
            }
            
            if target_entity_type:
                try:
                    app_label = app_label_map.get(target_entity_type.lower(), "core")
                    content_type = ContentType.objects.get(app_label=app_label, model=target_entity_type.lower())
                    model_class = content_type.model_class()
                    
                    if model_class is None:
                        raise ValueError(f"No model class for {target_entity_type}")
                    
                    target_entity = model_class.objects.filter(pk=entity_uuid).first()
                    
                    if not target_entity:
                        return Response(
                            {"success": False, "message": f"Existing {target_entity_type} not found with ID {existing_entity_id}"},
                            status=status.HTTP_404_NOT_FOUND,
                        )
                    
                    target_entity_name = str(getattr(target_entity, 'name', target_entity))[:200]
                    target_entity_slug = getattr(target_entity, 'slug', None)
                    
                except ContentType.DoesNotExist:
                    return Response(
                        {"success": False, "message": f"Unknown entity type: {target_entity_type}"},
                        status=status.HTTP_400_BAD_REQUEST,
                    )
                except Exception as e:
                    logger.warning(f"Failed to look up entity {target_entity_type}/{existing_entity_id}: {e}")
                    return Response(
                        {"success": False, "message": "Existing entity not found"},
                        status=status.HTTP_404_NOT_FOUND,
                    )
            else:
                # Try to find entity across common models
                for model_name, app_label in [("park", "parks"), ("ride", "rides"), ("company", "core")]:
                    try:
                        content_type = ContentType.objects.get(app_label=app_label, model=model_name)
                        model_class = content_type.model_class()
                        
                        if model_class is None:
                            continue
                        
                        target_entity = model_class.objects.filter(pk=entity_uuid).first()
                        if target_entity:
                            target_entity_type = model_name
                            target_entity_name = str(getattr(target_entity, 'name', target_entity))[:200]
                            target_entity_slug = getattr(target_entity, 'slug', None)
                            break
                    except Exception:
                        continue
                
                if not target_entity:
                    return Response(
                        {"success": False, "message": "Existing entity not found in any known model"},
                        status=status.HTTP_404_NOT_FOUND,
                    )
            
            # ================================================================
            # STEP 8: Update submission with atomic transaction
            # ================================================================
            try:
                with transaction.atomic():
                    # Re-fetch with lock to ensure no concurrent modifications
                    submission = EditSubmission.objects.select_for_update().get(pk=submission_uuid)
                    
                    # Double-check state hasn't changed
                    if submission.submission_type != "CREATE":
                        return Response(
                            {"success": False, "message": "Submission was already converted"},
                            status=status.HTTP_409_CONFLICT,
                        )
                    
                    # Update submission_type
                    submission.submission_type = "EDIT"
                    
                    # Link to existing entity via object_id
                    submission.object_id = entity_uuid
                    
                    # Store conversion metadata in changes
                    if not isinstance(submission.changes, dict):
                        submission.changes = {}
                    
                    submission.changes["_conversion_metadata"] = {
                        "converted_from": "new_entity_submission",
                        "original_action_type": "create",
                        "target_entity_type": target_entity_type,
                        "target_entity_id": str(entity_uuid),
                        "target_entity_name": target_entity_name,
                        "target_entity_slug": target_entity_slug,
                        "conversion_type": conversion_type,
                        "converted_by": user.username,
                        "converted_by_id": str(getattr(user, 'user_id', user.id)),
                        "converted_at": timezone.now().isoformat(),
                    }
                    
                    # Add moderator note (with length limit)
                    existing_notes = (submission.notes or "")[:self.MAX_NOTE_LENGTH]
                    conversion_note = f"[Converted CREATE to EDIT] for {target_entity_type}: {target_entity_name}"
                    if target_entity_slug:
                        conversion_note += f" ({target_entity_slug})"
                    conversion_note += f". Conversion type: {conversion_type}"
                    
                    new_notes = f"{existing_notes}\n\n{conversion_note}".strip()
                    submission.notes = new_notes[:self.MAX_NOTE_LENGTH]
                    
                    submission.save(update_fields=["submission_type", "object_id", "changes", "notes"])
                    
            except Exception as e:
                logger.error(f"Failed to update submission {submission_uuid}: {e}")
                return Response(
                    {"success": False, "message": "Failed to update submission"},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                )
            
            # ================================================================
            # STEP 9: Log to audit trail (outside transaction for reliability)
            # ================================================================
            try:
                log_business_event(
                    logger,
                    event_type="submission_converted_to_edit",
                    message=f"EditSubmission {submission.id} converted from CREATE to EDIT for {target_entity_type}#{entity_uuid}",
                    context={
                        "model": "EditSubmission",
                        "object_id": str(submission.id),
                        "item_id": str(item_id) if item_id else None,
                        "target_entity_type": target_entity_type,
                        "target_entity_id": str(entity_uuid),
                        "target_entity_name": target_entity_name,
                        "converted_by": user.username,
                        "conversion_type": conversion_type,
                    },
                    request=request,
                )
            except Exception as log_error:
                # Don't fail the request if logging fails
                logger.warning(f"Failed to log conversion event: {log_error}")
            
            # ================================================================
            # STEP 10: Return success response matching original format
            # ================================================================
            return Response({
                "success": True,
                "itemId": str(item_id) if item_id else str(submission.id),
                "submissionId": str(submission.id),
                "existingEntityId": str(entity_uuid),
                "existingEntityName": target_entity_name,
                "message": f"Converted submission item to EDIT for existing {target_entity_type}: {target_entity_name}",
            })
            
        except Exception as e:
            capture_and_log(e, "Convert submission to edit", source="moderation", request=request)
            return Response(
                {"success": False, "message": "Internal server error"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR,
            )


# ============================================================================
# Aggregated Moderation Stats View
# ============================================================================


from rest_framework.views import APIView


class ModerationStatsView(APIView):
    """
    View for aggregated moderation statistics.
    
    Returns comprehensive stats from all moderation models including
    reports, queue, actions, and bulk operations.
    """
    
    permission_classes = [CanViewModerationData]
    
    def get(self, request):
        """Get aggregated moderation statistics."""
        now = timezone.now()
        
        # Report stats
        reports = ModerationReport.objects.all()
        total_reports = reports.count()
        pending_reports = reports.filter(status="PENDING").count()
        resolved_reports = reports.filter(status="RESOLVED").count()
        
        # Calculate overdue reports
        overdue_reports = 0
        for report in reports.filter(status__in=["PENDING", "UNDER_REVIEW"]):
            sla_hours = {"URGENT": 2, "HIGH": 8, "MEDIUM": 24, "LOW": 72}
            hours_since_created = (now - report.created_at).total_seconds() / 3600
            threshold = sla_hours.get(report.priority, 72)
            if hours_since_created > threshold:
                overdue_reports += 1
        
        # Queue stats
        queue = ModerationQueue.objects.all()
        queue_size = queue.count()
        assigned_items = queue.filter(assigned_to__isnull=False).count()
        unassigned_items = queue.filter(assigned_to__isnull=True).count()
        
        # Action stats
        actions = ModerationAction.objects.all()
        total_actions = actions.count()
        active_actions = actions.filter(is_active=True).count()
        expired_actions = actions.filter(
            is_active=True, 
            expires_at__isnull=False, 
            expires_at__lt=now
        ).count()
        
        # Bulk operation stats
        bulk_ops = BulkOperation.objects.all()
        running_operations = bulk_ops.filter(status="RUNNING").count()
        completed_operations = bulk_ops.filter(status="COMPLETED").count()
        failed_operations = bulk_ops.filter(status="FAILED").count()
        
        # Average resolution time
        resolved_queryset = reports.filter(
            status="RESOLVED", 
            resolved_at__isnull=False
        )
        avg_resolution_time = 0
        if resolved_queryset.exists():
            total_time = sum([
                (r.resolved_at - r.created_at).total_seconds() / 3600
                for r in resolved_queryset if r.resolved_at
            ])
            avg_resolution_time = total_time / resolved_queryset.count()
        
        # Reports by priority and type
        reports_by_priority = dict(
            reports.values_list("priority").annotate(count=Count("id"))
        )
        reports_by_type = dict(
            reports.values_list("report_type").annotate(count=Count("id"))
        )
        
        stats_data = {
            # Report stats
            "total_reports": total_reports,
            "pending_reports": pending_reports,
            "resolved_reports": resolved_reports,
            "overdue_reports": overdue_reports,
            
            # Queue stats
            "queue_size": queue_size,
            "assigned_items": assigned_items,
            "unassigned_items": unassigned_items,
            
            # Action stats
            "total_actions": total_actions,
            "active_actions": active_actions,
            "expired_actions": expired_actions,
            
            # Bulk operation stats
            "running_operations": running_operations,
            "completed_operations": completed_operations,
            "failed_operations": failed_operations,
            
            # Performance metrics
            "average_resolution_time_hours": round(avg_resolution_time, 2),
            "reports_by_priority": reports_by_priority,
            "reports_by_type": reports_by_type,
            
            # Empty metrics array for frontend compatibility
            "metrics": [],
        }
        
        return Response(stats_data)
