from django.http import JsonResponse from django.shortcuts import get_object_or_404 from rest_framework import generics, permissions, status from rest_framework.exceptions import PermissionDenied from rest_framework.response import Response from rest_framework.views import APIView from .models import Booking from .serializers import BookingCreateSerializer, BookingSerializer from .services import is_bookable, transition_booking_status def health_check(request): return JsonResponse({"status": "ok", "service": "WaterTrek"}) class AvailabilityView(APIView): permission_classes = (permissions.AllowAny,) def get(self, request): equipment_item_id = request.query_params.get("equipment_item_id") adventure_offering_id = request.query_params.get("adventure_offering_id") starts_at = request.query_params.get("starts_at") ends_at = request.query_params.get("ends_at") if not starts_at or not ends_at: return Response( {"detail": "starts_at and ends_at are required query params."}, status=status.HTTP_400_BAD_REQUEST, ) if bool(equipment_item_id) == bool(adventure_offering_id): return Response( {"detail": "Provide exactly one target: equipment_item_id or adventure_offering_id."}, status=status.HTTP_400_BAD_REQUEST, ) payload = {"starts_at": starts_at, "ends_at": ends_at} if equipment_item_id: payload["equipment_item_id"] = equipment_item_id if adventure_offering_id: payload["adventure_offering_id"] = adventure_offering_id serializer = BookingCreateSerializer(data=payload) serializer.is_valid(raise_exception=True) equipment_item = serializer.validated_data.get("equipment_item_id") adventure_offering = serializer.validated_data.get("adventure_offering_id") starts_at_value = serializer.validated_data["starts_at"] ends_at_value = serializer.validated_data["ends_at"] conflict_filter = { "starts_at__lt": ends_at_value, "ends_at__gt": starts_at_value, "status__in": [Booking.Status.REQUESTED, Booking.Status.APPROVED, Booking.Status.CONFIRMED], } if equipment_item: conflict_filter["equipment_item"] = equipment_item else: conflict_filter["adventure_offering"] = adventure_offering conflicts = Booking.objects.filter(**conflict_filter).count() available = is_bookable( equipment_item=equipment_item, adventure_offering=adventure_offering, starts_at=starts_at_value, ends_at=ends_at_value, ) return Response( { "equipment_item_id": equipment_item.id if equipment_item else None, "adventure_offering_id": adventure_offering.id if adventure_offering else None, "starts_at": starts_at_value, "ends_at": ends_at_value, "is_available": available, "conflicts": conflicts, } ) class BookingCreateView(generics.CreateAPIView): serializer_class = BookingCreateSerializer permission_classes = (permissions.IsAuthenticated,) def perform_create(self, serializer): if not self.request.user.is_customer: raise PermissionDenied("Only customers can request bookings.") self.instance = serializer.save() def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) self.perform_create(serializer) return Response(BookingSerializer(self.instance).data, status=status.HTTP_201_CREATED) class BookingListView(generics.ListAPIView): serializer_class = BookingSerializer permission_classes = (permissions.IsAuthenticated,) def get_queryset(self): user = self.request.user if user.is_vendor and hasattr(user, "vendor_profile"): return Booking.objects.filter(vendor=user.vendor_profile).select_related( "customer", "vendor", "equipment_item", "adventure_offering" ) return Booking.objects.filter(customer=user).select_related("customer", "vendor", "equipment_item", "adventure_offering") class BookingDetailView(generics.RetrieveAPIView): serializer_class = BookingSerializer permission_classes = (permissions.IsAuthenticated,) def get_queryset(self): user = self.request.user if user.is_vendor and hasattr(user, "vendor_profile"): return Booking.objects.filter(vendor=user.vendor_profile).select_related( "customer", "vendor", "equipment_item", "adventure_offering" ) return Booking.objects.filter(customer=user).select_related("customer", "vendor", "equipment_item", "adventure_offering") class VendorApproveBookingView(APIView): permission_classes = (permissions.IsAuthenticated,) def post(self, request, pk): if not request.user.is_vendor or not hasattr(request.user, "vendor_profile"): raise PermissionDenied("Only vendors can approve bookings.") booking = get_object_or_404(Booking, pk=pk, vendor=request.user.vendor_profile) if booking.status != Booking.Status.REQUESTED: return Response({"detail": "Only requested bookings can be approved."}, status=status.HTTP_400_BAD_REQUEST) transition_booking_status( booking=booking, to_status=Booking.Status.APPROVED, actor=request.user, note=request.data.get("note", "Booking approved."), vendor_notes=request.data.get("vendor_notes", booking.vendor_notes), ) return Response(BookingSerializer(booking).data) class VendorDeclineBookingView(APIView): permission_classes = (permissions.IsAuthenticated,) def post(self, request, pk): if not request.user.is_vendor or not hasattr(request.user, "vendor_profile"): raise PermissionDenied("Only vendors can decline bookings.") booking = get_object_or_404(Booking, pk=pk, vendor=request.user.vendor_profile) if booking.status != Booking.Status.REQUESTED: return Response({"detail": "Only requested bookings can be declined."}, status=status.HTTP_400_BAD_REQUEST) transition_booking_status( booking=booking, to_status=Booking.Status.DECLINED, actor=request.user, note=request.data.get("note", "Booking declined."), vendor_notes=request.data.get("vendor_notes", booking.vendor_notes), ) return Response(BookingSerializer(booking).data) class CustomerCancelBookingView(APIView): permission_classes = (permissions.IsAuthenticated,) def post(self, request, pk): booking = get_object_or_404(Booking, pk=pk, customer=request.user) if booking.status in [Booking.Status.CANCELLED, Booking.Status.DECLINED]: return Response({"detail": "This booking cannot be cancelled."}, status=status.HTTP_400_BAD_REQUEST) transition_booking_status( booking=booking, to_status=Booking.Status.CANCELLED, actor=request.user, note=request.data.get("note", "Booking cancelled by customer."), ) return Response(BookingSerializer(booking).data)