from django.shortcuts import get_object_or_404
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

from clubs.models import Club
from players.models import Player

from .constants import HallOfFameEntityType
from .exceptions import OfficialHistoryError
from .models import (
    OfficialHallOfFameEntry,
    OfficialHistoricalRecord,
    OfficialHistoryProjection,
    OfficialHonor,
    OfficialMatchArchive,
)
from .serializers import (
    OfficialClubSeasonStatisticSerializer,
    OfficialCompetitionArchiveSerializer,
    OfficialHallOfFameEntrySerializer,
    OfficialHistoricalRecordSerializer,
    OfficialHistoryProjectionSerializer,
    OfficialHonorSerializer,
    OfficialLeagueStandingRowSerializer,
    OfficialMatchArchiveSerializer,
    OfficialPlayerSeasonStatisticSerializer,
)
from .services import (
    HallOfFameService,
    OfficialHistoryIntegrityService,
    OfficialHistoryProjectionService,
    OfficialHistorySelector,
    OfficialMatchStatisticsService,
)


def _latest_or_404(season_id):
    projection = OfficialHistoryProjectionService.latest_for_season(season_id)
    if projection is None:
        from rest_framework.exceptions import NotFound
        raise NotFound("This season does not yet have an official historical projection.")
    return projection


class SeasonArchiveView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request, season_id):
        projection = _latest_or_404(season_id)
        return Response({
            "projection": OfficialHistoryProjectionSerializer(projection).data,
            "competitions": OfficialCompetitionArchiveSerializer(projection.competitions.all(), many=True).data,
            "honors": OfficialHonorSerializer(projection.honors.all(), many=True).data,
            "records": OfficialHistoricalRecordSerializer(projection.records.all(), many=True).data,
            "counts": projection.projection_payload.get("counts") or {},
        })


class SeasonArchiveCollectionView(APIView):
    permission_classes = [IsAuthenticated]
    serializers = {
        "matches": ("matches", OfficialMatchArchiveSerializer),
        "standings": ("standing_rows", OfficialLeagueStandingRowSerializer),
        "club-statistics": ("club_statistics", OfficialClubSeasonStatisticSerializer),
        "player-statistics": ("player_statistics", OfficialPlayerSeasonStatisticSerializer),
        "honors": ("honors", OfficialHonorSerializer),
        "records": ("records", OfficialHistoricalRecordSerializer),
    }

    def get(self, request, season_id, collection):
        projection = _latest_or_404(season_id)
        if collection not in self.serializers:
            return Response({"detail": "Unknown official archive collection."}, status=status.HTTP_404_NOT_FOUND)
        related_name, serializer = self.serializers[collection]
        queryset = getattr(projection, related_name).all()
        try:
            limit = min(max(int(request.query_params.get("limit", 100)), 1), 500)
            offset = max(int(request.query_params.get("offset", 0)), 0)
        except (TypeError, ValueError):
            return Response({"detail": "limit and offset must be integers."}, status=status.HTTP_400_BAD_REQUEST)
        total = queryset.count()
        page = queryset[offset:offset + limit]
        return Response({
            "projection_id": str(projection.pk),
            "projection_hash": projection.projection_hash,
            "count": total,
            "limit": limit,
            "offset": offset,
            "results": serializer(page, many=True).data,
        })


class OfficialMatchStatisticsView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request, match_id):
        latest_ids = OfficialHistorySelector.latest_projection_ids()
        archive = get_object_or_404(
            OfficialMatchArchive.objects.filter(projection_id__in=latest_ids).select_related("projection"),
            match_id=match_id,
        )
        try:
            payload = OfficialMatchStatisticsService.get(archive)
        except OfficialHistoryError as exc:
            return Response({"detail": str(exc)}, status=status.HTTP_409_CONFLICT)
        return Response({
            "projection_id": str(archive.projection_id),
            "projection_hash": archive.projection.projection_hash,
            "match_archive_hash": archive.archive_hash,
            **payload,
        })


class ClubHistoryView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request, club_id):
        club = get_object_or_404(Club, pk=club_id)
        latest_ids = OfficialHistorySelector.latest_projection_ids()
        stats = club.official_season_statistics.filter(projection_id__in=latest_ids).select_related("projection__season").order_by("projection__season__start_date")
        honors = club.official_honors.filter(projection_id__in=latest_ids).select_related("projection__season").order_by("projection__season__start_date")
        records = club.official_records.filter(projection_id__in=latest_ids).select_related("projection__season").order_by("projection__season__start_date")
        return Response({
            "club_id": str(club.pk),
            "club_name": club.name,
            "season_statistics": OfficialClubSeasonStatisticSerializer(stats, many=True).data,
            "honors": OfficialHonorSerializer(honors, many=True).data,
            "records": OfficialHistoricalRecordSerializer(records, many=True).data,
        })


class PlayerHistoryView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request, player_id):
        player = get_object_or_404(Player, pk=player_id)
        latest_ids = OfficialHistorySelector.latest_projection_ids()
        stats = player.official_season_statistics.filter(projection_id__in=latest_ids).select_related("projection__season").order_by("projection__season__start_date")
        honors = player.official_honors.filter(projection_id__in=latest_ids).select_related("projection__season").order_by("projection__season__start_date")
        records = player.official_records.filter(projection_id__in=latest_ids).select_related("projection__season").order_by("projection__season__start_date")
        return Response({
            "player_id": str(player.pk),
            "player_name": f"{player.first_name} {player.last_name}".strip(),
            "season_statistics": OfficialPlayerSeasonStatisticSerializer(stats, many=True).data,
            "honors": OfficialHonorSerializer(honors, many=True).data,
            "records": OfficialHistoricalRecordSerializer(records, many=True).data,
        })


class OfficialRecordsView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request):
        qs = OfficialHistorySelector.current_records().select_related("projection__season")
        metric = request.query_params.get("metric")
        scope = request.query_params.get("scope")
        if metric:
            qs = qs.filter(metric_key=metric)
        if scope:
            qs = qs.filter(scope_key=scope)
        qs = qs.order_by("metric_key", "scope_key", "-value", "projection__season__start_date")
        return Response(OfficialHistoricalRecordSerializer(qs, many=True).data)


class HallOfFameView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request):
        return Response(OfficialHallOfFameEntrySerializer(OfficialHallOfFameEntry.objects.select_related("club", "player", "inducted_season"), many=True).data)


class HallOfFameInductView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request):
        projection = get_object_or_404(OfficialHistoryProjection, pk=request.data.get("projection_id"))
        entity_type = request.data.get("entity_type")
        club = None
        player = None
        if entity_type == HallOfFameEntityType.CLUB:
            club = get_object_or_404(Club, pk=request.data.get("club_id"))
        elif entity_type == HallOfFameEntityType.PLAYER:
            player = get_object_or_404(Player, pk=request.data.get("player_id"))
        try:
            entry = HallOfFameService.induct(
                projection=projection,
                actor=request.user,
                entity_type=entity_type,
                club=club,
                player=player,
                reason=request.data.get("reason") or "",
                evidence_ids=request.data.get("evidence_ids") or [],
            )
        except OfficialHistoryError as exc:
            return Response({"detail": str(exc)}, status=status.HTTP_409_CONFLICT)
        return Response(OfficialHallOfFameEntrySerializer(entry).data, status=status.HTTP_201_CREATED)


class ProjectionIntegrityView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request, projection_id):
        projection = get_object_or_404(OfficialHistoryProjection, pk=projection_id)
        result = OfficialHistoryIntegrityService.verify_projection(projection)
        return Response({"projection_id": str(projection.pk), "projection_hash": projection.projection_hash, **result})
