from django.shortcuts import get_object_or_404 from rest_framework import status from rest_framework.decorators import api_view, authentication_classes, permission_classes, throttle_classes from rest_framework.permissions import AllowAny from rest_framework.response import Response from rest_framework.throttling import AnonRateThrottle from shows.models import Performance from .serializers import ( ReservationConfirmResponseSerializer, ReservationConfirmSerializer, ReservationCreateResponseSerializer, ReservationCreateSerializer, ReservationQRResponseSerializer, ) from .services import ( AlreadyConfirmedReservation, ExpiredToken, InvalidToken, NotEnoughSeats, PerformanceNotAvailable, ReservationNotConfirmed, confirm_reservation_from_token, create_pending_reservation, retrieve_reservation_qr_from_token, ) class ReservationCreateThrottle(AnonRateThrottle): scope = "reservation_create" class ReservationConfirmThrottle(AnonRateThrottle): scope = "reservation_confirm" @api_view(["POST"]) @authentication_classes([]) @permission_classes([AllowAny]) @throttle_classes([ReservationCreateThrottle]) def create_reservation(request, performance_id): get_object_or_404(Performance, pk=performance_id, show__is_published=True) serializer = ReservationCreateSerializer(data=request.data) serializer.is_valid(raise_exception=True) try: result = create_pending_reservation( performance_id=performance_id, name=serializer.validated_data["name"], email=serializer.validated_data["email"], phone=serializer.validated_data.get("phone", ""), party_size=serializer.validated_data["party_size"], notes=serializer.validated_data.get("notes", ""), ) except (NotEnoughSeats, PerformanceNotAvailable) as exc: return Response( {"status": "booking_unavailable", "detail": str(exc)}, status=status.HTTP_409_CONFLICT, ) response_serializer = ReservationCreateResponseSerializer( { "id": result.reservation.id, "status": result.reservation.status, "performance_id": result.reservation.performance_id, "party_size": result.reservation.party_size, "message": "Reservation created. Please check your email to confirm it.", } ) return Response(response_serializer.data, status=status.HTTP_201_CREATED) @api_view(["GET", "POST"]) @throttle_classes([ReservationConfirmThrottle]) def confirm_reservation(request): payload = request.query_params if request.method == "GET" else request.data serializer = ReservationConfirmSerializer(data=payload) serializer.is_valid(raise_exception=True) try: result = confirm_reservation_from_token(serializer.validated_data["token"]) except InvalidToken as exc: return Response( {"status": "invalid_token", "detail": str(exc)}, status=status.HTTP_404_NOT_FOUND, ) except ExpiredToken as exc: return Response( {"status": "token_expired", "detail": str(exc)}, status=status.HTTP_410_GONE, ) except NotEnoughSeats as exc: return Response( {"status": "not_enough_seats", "detail": str(exc)}, status=status.HTTP_409_CONFLICT, ) except AlreadyConfirmedReservation as exc: return Response( {"status": "already_confirmed", "detail": str(exc)}, status=status.HTTP_409_CONFLICT, ) response_serializer = ReservationConfirmResponseSerializer(result) return Response(response_serializer.data) @api_view(["GET"]) def retrieve_reservation_qr(request): serializer = ReservationConfirmSerializer(data=request.query_params) serializer.is_valid(raise_exception=True) try: result = retrieve_reservation_qr_from_token(serializer.validated_data["token"]) except InvalidToken as exc: return Response( {"status": "invalid_token", "detail": str(exc)}, status=status.HTTP_404_NOT_FOUND, ) except ReservationNotConfirmed as exc: return Response( {"status": "reservation_not_confirmed", "detail": str(exc)}, status=status.HTTP_409_CONFLICT, ) response_serializer = ReservationQRResponseSerializer(result) return Response(response_serializer.data)