"""
API viewsets for the ride ranking system.
"""

from typing import TYPE_CHECKING, Any, cast

from django.db.models import Q, QuerySet
from django.utils import timezone
from django_filters.rest_framework import DjangoFilterBackend
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiParameter, extend_schema, extend_schema_view
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.filters import OrderingFilter
from rest_framework.permissions import AllowAny, IsAuthenticatedOrReadOnly
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import BaseSerializer
from rest_framework.views import APIView
from rest_framework.viewsets import ReadOnlyModelViewSet

if TYPE_CHECKING:
    pass

# Import models inside methods to avoid Django initialization issues
import contextlib

from .serializers_rankings import (
    RankingSnapshotSerializer,
    RankingStatsSerializer,
    RideRankingDetailSerializer,
    RideRankingSerializer,
)


@extend_schema_view(
    list=extend_schema(
        summary="List ride rankings",
        description="Get the current ride rankings calculated using the Internet Roller Coaster Poll algorithm.",
        parameters=[
            OpenApiParameter(
                name="category",
                type=OpenApiTypes.STR,
                location=OpenApiParameter.QUERY,
                description="Filter by ride category (RC, DR, FR, WR, TR, OT)",
                enum=["RC", "DR", "FR", "WR", "TR", "OT"],
            ),
            OpenApiParameter(
                name="min_riders",
                type=OpenApiTypes.INT,
                location=OpenApiParameter.QUERY,
                description="Minimum number of mutual riders required",
            ),
            OpenApiParameter(
                name="park",
                type=OpenApiTypes.STR,
                location=OpenApiParameter.QUERY,
                description="Filter by park slug",
            ),
            OpenApiParameter(
                name="ordering",
                type=OpenApiTypes.STR,
                location=OpenApiParameter.QUERY,
                description="Order results (rank, -rank, winning_percentage, -winning_percentage)",
            ),
        ],
        responses={200: RideRankingSerializer(many=True)},
        tags=["Rankings"],
    ),
    retrieve=extend_schema(
        summary="Get ranking details",
        description="Get detailed ranking information for a specific ride.",
        responses={
            200: RideRankingDetailSerializer,
            404: OpenApiTypes.OBJECT,
        },
        tags=["Rankings"],
    ),
    history=extend_schema(
        summary="Get ranking history",
        description="Get historical ranking data for a specific ride.",
        responses={200: RankingSnapshotSerializer(many=True)},
        tags=["Rankings"],
    ),
    statistics=extend_schema(
        summary="Get ranking statistics",
        description="Get overall statistics about the ranking system.",
        responses={200: RankingStatsSerializer},
        tags=["Rankings", "Statistics"],
    ),
)
class RideRankingViewSet(ReadOnlyModelViewSet):
    """
    ViewSet for ride rankings.

    Provides access to ride rankings calculated using the Internet Roller Coaster Poll algorithm.
    Rankings are updated daily and based on pairwise comparisons of user ratings.
    """

    permission_classes = [AllowAny]
    lookup_field = "ride__slug"
    lookup_url_kwarg = "ride_slug"
    filter_backends = [DjangoFilterBackend, OrderingFilter]
    filterset_fields = ["ride__category"]
    ordering_fields = [
        "rank",
        "winning_percentage",
        "mutual_riders_count",
        "average_rating",
    ]
    ordering = ["rank"]

    def get_queryset(self) -> QuerySet[Any]:  # type: ignore
        """Get rankings with optimized queries."""
        from apps.rides.models import RideRanking

        queryset = RideRanking.objects.select_related(
            "ride", "ride__park", "ride__park__location", "ride__manufacturer"
        )

        # Cast self.request to DRF Request so type checker recognizes query_params
        request = cast(Request, self.request)

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

        # Filter by minimum mutual riders
        min_riders = request.query_params.get("min_riders")
        if min_riders:
            with contextlib.suppress(ValueError):
                queryset = queryset.filter(mutual_riders_count__gte=int(min_riders))

        # Filter by park
        park_slug = request.query_params.get("park")
        if park_slug:
            queryset = queryset.filter(ride__park__slug=park_slug)

        return queryset

    def get_serializer_class(self) -> Any:  # type: ignore[override]
        """Use different serializers for list vs detail."""
        if self.action == "retrieve":
            return cast(type[BaseSerializer], RideRankingDetailSerializer)
        elif self.action == "history":
            return cast(type[BaseSerializer], RankingSnapshotSerializer)
        elif self.action == "statistics":
            return cast(type[BaseSerializer], RankingStatsSerializer)
        return cast(type[BaseSerializer], RideRankingSerializer)

    @action(detail=True, methods=["get"])
    def history(self, request, ride_slug=None):
        """Get ranking history for a specific ride."""
        from apps.rides.models import RankingSnapshot

        ranking = self.get_object()
        history = RankingSnapshot.objects.filter(ride=ranking.ride).order_by("-snapshot_date")[:90]  # Last 3 months

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

    @action(detail=False, methods=["get"])
    def statistics(self, request):
        """Get overall ranking system statistics."""
        from apps.rides.models import RankingSnapshot, RidePairComparison, RideRanking

        total_rankings = RideRanking.objects.count()
        total_comparisons = RidePairComparison.objects.count()

        # Get last calculation time
        latest_ranking = RideRanking.objects.order_by("-last_calculated").first()
        last_calc_time = latest_ranking.last_calculated if latest_ranking else None

        # Get top rated ride
        top_rated = RideRanking.objects.select_related("ride", "ride__park").first()

        # Get most compared ride
        most_compared = RideRanking.objects.select_related("ride", "ride__park").order_by("-comparison_count").first()

        # Get biggest rank change (last 7 days)
        from datetime import timedelta

        week_ago = timezone.now().date() - timedelta(days=7)

        biggest_change = None
        max_change = 0

        current_rankings = RideRanking.objects.select_related("ride")
        for ranking in current_rankings[:100]:  # Check top 100 for performance
            old_snapshot = (
                RankingSnapshot.objects.filter(ride=ranking.ride, snapshot_date__lte=week_ago)
                .order_by("-snapshot_date")
                .first()
            )

            if old_snapshot:
                change = abs(old_snapshot.rank - ranking.rank)
                if change > max_change:
                    max_change = change
                    biggest_change = {
                        "ride": {
                            "id": ranking.ride.id,
                            "name": ranking.ride.name,
                            "slug": ranking.ride.slug,
                        },
                        "current_rank": ranking.rank,
                        "previous_rank": old_snapshot.rank,
                        "change": old_snapshot.rank - ranking.rank,
                    }

        stats = {
            "total_ranked_rides": total_rankings,
            "total_comparisons": total_comparisons,
            "last_calculation_time": last_calc_time,
            "calculation_duration": None,  # Would need to track this separately
            "top_rated_ride": (
                {
                    "id": top_rated.ride.id,
                    "name": top_rated.ride.name,
                    "slug": top_rated.ride.slug,
                    "park": top_rated.ride.park.name,
                    "rank": top_rated.rank,
                    "winning_percentage": float(top_rated.winning_percentage),
                    "average_rating": (float(top_rated.average_rating) if top_rated.average_rating else None),
                }
                if top_rated
                else None
            ),
            "most_compared_ride": (
                {
                    "id": most_compared.ride.id,
                    "name": most_compared.ride.name,
                    "slug": most_compared.ride.slug,
                    "park": most_compared.ride.park.name,
                    "comparison_count": most_compared.comparison_count,
                }
                if most_compared
                else None
            ),
            "biggest_rank_change": biggest_change,
        }

        serializer = RankingStatsSerializer(stats)
        return Response(serializer.data)

    @extend_schema(
        summary="Get ride comparisons",
        description="Get head-to-head comparisons for a specific ride",
        responses={200: OpenApiTypes.OBJECT},
        tags=["Rankings"],
    )
    @action(detail=True, methods=["get"])
    def comparisons(self, request, ride_slug=None):
        """Get head-to-head comparisons for a specific ride."""
        from apps.rides.models import RidePairComparison

        ranking = self.get_object()

        comparisons = (
            RidePairComparison.objects.filter(Q(ride_a=ranking.ride) | Q(ride_b=ranking.ride))
            .select_related("ride_a", "ride_b", "ride_a__park", "ride_b__park")
            .order_by("-mutual_riders_count")[:50]
        )

        results = []
        for comp in comparisons:
            if comp.ride_a == ranking.ride:
                opponent = comp.ride_b
                wins = comp.ride_a_wins
                losses = comp.ride_b_wins
            else:
                opponent = comp.ride_a
                wins = comp.ride_b_wins
                losses = comp.ride_a_wins

            result = "win" if wins > losses else "loss" if losses > wins else "tie"

            results.append(
                {
                    "opponent": {
                        "id": opponent.id,
                        "name": opponent.name,
                        "slug": opponent.slug,
                        "park": {
                            "id": opponent.park.id,
                            "name": opponent.park.name,
                            "slug": opponent.park.slug,
                        },
                    },
                    "wins": wins,
                    "losses": losses,
                    "ties": comp.ties,
                    "result": result,
                    "mutual_riders": comp.mutual_riders_count,
                    "ride_a_avg_rating": (float(comp.ride_a_avg_rating) if comp.ride_a_avg_rating else None),
                    "ride_b_avg_rating": (float(comp.ride_b_avg_rating) if comp.ride_b_avg_rating else None),
                }
            )

        return Response(results)


@extend_schema(
    summary="Trigger ranking calculation",
    description="Manually trigger a ranking calculation (admin only).",
    request=None,
    responses={
        200: OpenApiTypes.OBJECT,
        403: OpenApiTypes.OBJECT,
    },
    tags=["Rankings", "Admin"],
)
class TriggerRankingCalculationView(APIView):
    """
    Admin endpoint to manually trigger ranking calculation.
    """

    permission_classes = [IsAuthenticatedOrReadOnly]

    def post(self, request):
        """Trigger ranking calculation."""
        if not request.user.is_staff:
            return Response({"detail": "Admin access required"}, status=status.HTTP_403_FORBIDDEN)

        # Replace direct import with a guarded runtime import to avoid static-analysis/initialization errors
        try:
            from apps.rides.services import RideRankingService  # type: ignore
        except Exception:
            RideRankingService = None  # type: ignore

        # Attempt a dynamic import as a fallback if the direct import failed
        if RideRankingService is None:
            try:
                import importlib

                _services_mod = importlib.import_module("apps.rides.services")
                RideRankingService = getattr(_services_mod, "RideRankingService", None)
            except Exception:
                RideRankingService = None

        if not RideRankingService:
            return Response(
                {"detail": "Ranking service unavailable"},
                status=status.HTTP_503_SERVICE_UNAVAILABLE,
            )

        category = request.data.get("category")

        service = RideRankingService()
        result = service.update_all_rankings(category=category)

        return Response(result)
