"""
Ride review API views for ThrillWiki API v1 (nested under parks).

This module contains ride review ViewSet following the parks pattern for domain consistency.
Provides CRUD operations for ride reviews nested under parks/{park_slug}/rides/{ride_slug}/reviews/
"""

import logging
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    pass

from django.core.exceptions import PermissionDenied
from django.db.models import Avg
from django.utils import timezone
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema, extend_schema_view
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.exceptions import NotFound, ValidationError
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet

from apps.api.v1.serializers.ride_reviews import (
    RideReviewCreateInputSerializer,
    RideReviewListOutputSerializer,
    RideReviewModerationInputSerializer,
    RideReviewOutputSerializer,
    RideReviewStatsOutputSerializer,
    RideReviewUpdateInputSerializer,
)
from apps.core.utils import capture_and_log
from apps.parks.models import Park
from apps.rides.models import Ride
from apps.rides.models.reviews import RideReview

logger = logging.getLogger(__name__)


@extend_schema_view(
    list=extend_schema(
        summary="List ride reviews",
        description="Retrieve a paginated list of ride reviews with filtering capabilities.",
        responses={200: RideReviewListOutputSerializer(many=True)},
        tags=["Ride Reviews"],
    ),
    create=extend_schema(
        summary="Create ride review",
        description="Create a new review for a ride. Requires authentication.",
        request=RideReviewCreateInputSerializer,
        responses={
            201: RideReviewOutputSerializer,
            400: OpenApiTypes.OBJECT,
            401: OpenApiTypes.OBJECT,
        },
        tags=["Ride Reviews"],
    ),
    retrieve=extend_schema(
        summary="Get ride review details",
        description="Retrieve detailed information about a specific ride review.",
        responses={
            200: RideReviewOutputSerializer,
            404: OpenApiTypes.OBJECT,
        },
        tags=["Ride Reviews"],
    ),
    update=extend_schema(
        summary="Update ride review",
        description="Update ride review information. Requires authentication and ownership or admin privileges.",
        request=RideReviewUpdateInputSerializer,
        responses={
            200: RideReviewOutputSerializer,
            400: OpenApiTypes.OBJECT,
            401: OpenApiTypes.OBJECT,
            403: OpenApiTypes.OBJECT,
            404: OpenApiTypes.OBJECT,
        },
        tags=["Ride Reviews"],
    ),
    partial_update=extend_schema(
        summary="Partially update ride review",
        description="Partially update ride review information. Requires authentication and ownership or admin privileges.",
        request=RideReviewUpdateInputSerializer,
        responses={
            200: RideReviewOutputSerializer,
            400: OpenApiTypes.OBJECT,
            401: OpenApiTypes.OBJECT,
            403: OpenApiTypes.OBJECT,
            404: OpenApiTypes.OBJECT,
        },
        tags=["Ride Reviews"],
    ),
    destroy=extend_schema(
        summary="Delete ride review",
        description="Delete a ride review. Requires authentication and ownership or admin privileges.",
        responses={
            204: None,
            401: OpenApiTypes.OBJECT,
            403: OpenApiTypes.OBJECT,
            404: OpenApiTypes.OBJECT,
        },
        tags=["Ride Reviews"],
    ),
)
class RideReviewViewSet(ModelViewSet):
    """
    ViewSet for managing ride reviews with full CRUD operations.

    Provides CRUD operations for ride reviews with proper permission checking.
    Includes advanced features like bulk moderation and statistics.
    """

    lookup_field = "id"

    def get_permissions(self):
        """Set permissions based on action."""
        permission_classes = [AllowAny] if self.action in ["list", "retrieve", "stats"] else [IsAuthenticated]
        return [permission() for permission in permission_classes]

    def get_queryset(self):
        """Get reviews for the current ride with optimized queries."""
        queryset = RideReview.objects.select_related("ride", "ride__park", "user", "user__profile")

        # Filter by park and ride from URL kwargs
        park_slug = self.kwargs.get("park_slug")
        ride_slug = self.kwargs.get("ride_slug")

        if park_slug and ride_slug:
            try:
                park, _ = Park.get_by_slug(park_slug)
                ride, _ = Ride.get_by_slug(ride_slug, park=park)
                queryset = queryset.filter(ride=ride)
            except (Park.DoesNotExist, Ride.DoesNotExist):
                # Return empty queryset if park or ride not found
                return queryset.none()

        # Filter published reviews for non-staff users
        if not (hasattr(self.request, "user") and getattr(self.request.user, "is_staff", False)):
            queryset = queryset.filter(is_published=True)

        return queryset.order_by("-created_at")

    def get_serializer_class(self):
        """Return appropriate serializer based on action."""
        if self.action == "list":
            return RideReviewListOutputSerializer
        elif self.action == "create":
            return RideReviewCreateInputSerializer
        elif self.action in ["update", "partial_update"]:
            return RideReviewUpdateInputSerializer
        else:
            return RideReviewOutputSerializer

    def perform_create(self, serializer):
        """Create a new ride review."""
        park_slug = self.kwargs.get("park_slug")
        ride_slug = self.kwargs.get("ride_slug")

        if not park_slug or not ride_slug:
            raise ValidationError("Park and ride slugs are required")

        try:
            park, _ = Park.get_by_slug(park_slug)
            ride, _ = Ride.get_by_slug(ride_slug, park=park)
        except Park.DoesNotExist:
            raise NotFound("Park not found") from None
        except Ride.DoesNotExist:
            raise NotFound("Ride not found at this park") from None

        # Check if user already has a review for this ride
        if RideReview.objects.filter(ride=ride, user=self.request.user).exists():
            raise ValidationError("You have already reviewed this ride")

        try:
            # Save the review
            review = serializer.save(
                ride=ride, user=self.request.user, is_published=True  # Auto-publish for now, can add moderation later
            )

            logger.info(f"Created ride review {review.id} for ride {ride.name} by user {self.request.user.username}")

        except Exception as e:
            capture_and_log(e, 'Create ride review', source='api', request=self.request)
            raise ValidationError(f"Failed to create review: {str(e)}") from None

    def perform_update(self, serializer):
        """Update ride review with permission checking."""
        instance = self.get_object()

        # Check permissions - allow owner or staff
        if not (self.request.user == instance.user or getattr(self.request.user, "is_staff", False)):
            raise PermissionDenied("You can only edit your own reviews or be an admin.")

        try:
            serializer.save()
            logger.info(f"Updated ride review {instance.id} by user {self.request.user.username}")
        except Exception as e:
            capture_and_log(e, 'Update ride review', source='api', request=self.request)
            raise ValidationError(f"Failed to update review: {str(e)}") from None

    def perform_destroy(self, instance):
        """Delete ride review with permission checking."""
        # Check permissions - allow owner or staff
        if not (self.request.user == instance.user or getattr(self.request.user, "is_staff", False)):
            raise PermissionDenied("You can only delete your own reviews or be an admin.")

        try:
            logger.info(f"Deleting ride review {instance.id} by user {self.request.user.username}")
            instance.delete()
        except Exception as e:
            capture_and_log(e, 'Delete ride review', source='api', request=self.request)
            raise ValidationError(f"Failed to delete review: {str(e)}") from None

    @extend_schema(
        summary="Get ride review statistics",
        description="Get review statistics for the ride",
        responses={
            200: RideReviewStatsOutputSerializer,
            404: OpenApiTypes.OBJECT,
            500: OpenApiTypes.OBJECT,
        },
        tags=["Ride Reviews"],
    )
    @action(detail=False, methods=["get"])
    def stats(self, request, **kwargs):
        """Get review statistics for the ride."""
        park_slug = self.kwargs.get("park_slug")
        ride_slug = self.kwargs.get("ride_slug")

        if not park_slug or not ride_slug:
            return Response(
                {"detail": "Park and ride slugs are required"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        try:
            park, _ = Park.get_by_slug(park_slug)
            ride, _ = Ride.get_by_slug(ride_slug, park=park)
        except Park.DoesNotExist:
            return Response(
                {"detail": "Park not found"},
                status=status.HTTP_404_NOT_FOUND,
            )
        except Ride.DoesNotExist:
            return Response(
                {"detail": "Ride not found at this park"},
                status=status.HTTP_404_NOT_FOUND,
            )

        try:
            # Get review statistics
            reviews = RideReview.objects.filter(ride=ride, is_published=True)

            total_reviews = reviews.count()
            published_reviews = total_reviews  # Since we're filtering published
            pending_reviews = RideReview.objects.filter(ride=ride, is_published=False).count()

            # Calculate average rating
            avg_rating = reviews.aggregate(avg_rating=Avg("rating"))["avg_rating"]

            # Get rating distribution
            rating_distribution = {}
            for i in range(1, 11):
                rating_distribution[str(i)] = reviews.filter(rating=i).count()

            # Get recent reviews count (last 30 days)
            from datetime import timedelta

            thirty_days_ago = timezone.now() - timedelta(days=30)
            recent_reviews = reviews.filter(created_at__gte=thirty_days_ago).count()

            stats = {
                "total_reviews": total_reviews,
                "published_reviews": published_reviews,
                "pending_reviews": pending_reviews,
                "average_rating": round(avg_rating, 2) if avg_rating else None,
                "rating_distribution": rating_distribution,
                "recent_reviews": recent_reviews,
            }

            serializer = RideReviewStatsOutputSerializer(stats)
            return Response(serializer.data, status=status.HTTP_200_OK)

        except Exception as e:
            capture_and_log(e, 'Get ride review stats', source='api', request=request)
            return Response(
                {"detail": f"Failed to get review statistics: {str(e)}"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR,
            )

    @extend_schema(
        summary="Bulk moderate reviews",
        description="Bulk moderate multiple ride reviews (admin only)",
        request=RideReviewModerationInputSerializer,
        responses={
            200: OpenApiTypes.OBJECT,
            400: OpenApiTypes.OBJECT,
            403: OpenApiTypes.OBJECT,
        },
        tags=["Ride Reviews"],
    )
    @action(detail=False, methods=["post"], permission_classes=[IsAuthenticated])
    def moderate(self, request, **kwargs):
        """Bulk moderate multiple reviews (admin only)."""
        if not getattr(request.user, "is_staff", False):
            raise PermissionDenied("Only administrators can moderate reviews.")

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

        validated_data = serializer.validated_data
        review_ids = validated_data.get("review_ids")
        action_type = validated_data.get("action")
        moderation_notes = validated_data.get("moderation_notes", "")

        park_slug = self.kwargs.get("park_slug")
        ride_slug = self.kwargs.get("ride_slug")

        try:
            # Filter reviews to only those belonging to this ride
            reviews_queryset = RideReview.objects.filter(id__in=review_ids)
            if park_slug and ride_slug:
                park, _ = Park.get_by_slug(park_slug)
                ride, _ = Ride.get_by_slug(ride_slug, park=park)
                reviews_queryset = reviews_queryset.filter(ride=ride)

            if action_type == "publish":
                updated_count = reviews_queryset.update(
                    is_published=True,
                    moderated_by=request.user,
                    moderated_at=timezone.now(),
                    moderation_notes=moderation_notes,
                )
                message = f"Successfully published {updated_count} reviews"
            elif action_type == "unpublish":
                updated_count = reviews_queryset.update(
                    is_published=False,
                    moderated_by=request.user,
                    moderated_at=timezone.now(),
                    moderation_notes=moderation_notes,
                )
                message = f"Successfully unpublished {updated_count} reviews"
            elif action_type == "delete":
                updated_count = reviews_queryset.count()
                reviews_queryset.delete()
                message = f"Successfully deleted {updated_count} reviews"
            else:
                return Response(
                    {"detail": "Invalid action type"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            return Response(
                {
                    "detail": message,
                    "updated_count": updated_count,
                },
                status=status.HTTP_200_OK,
            )

        except Exception as e:
            capture_and_log(e, 'Bulk review moderation', source='api', request=request)
            return Response(
                {"detail": f"Failed to moderate reviews: {str(e)}"},
                status=status.HTTP_400_BAD_REQUEST,
            )
