from django.contrib.gis.db.models.functions import Distance from django.contrib.gis.geos import Point from django.contrib.gis.measure import D from django.shortcuts import get_object_or_404 from drf_spectacular.utils import OpenApiParameter, extend_schema, extend_schema_view from rest_framework import mixins, status, viewsets from rest_framework.permissions import AllowAny, IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView from apps.gooyal_oauth2.rest_framework import IsAuthenticatedOrTokenMatchesOASRequirements from apps.core.permissions import IsStoreOwner from .filters import StoreFilter from .models import Store, StoreCategory, StoreWorkingHours from .serializers import ( SellerStoreSerializer, StoreCategorySerializer, StoreDetailSerializer, StoreListSerializer, StoreWorkingHoursSerializer, ) class StoreCategoryViewSet(mixins.ListModelMixin, viewsets.GenericViewSet): schema_tags = ['Stores'] permission_classes = [AllowAny] serializer_class = StoreCategorySerializer queryset = StoreCategory.objects.all() @extend_schema_view( list=extend_schema( parameters=[ OpenApiParameter('lat', float, description='Latitude; used with `lng` to sort by distance (15km radius).'), OpenApiParameter('lng', float, description='Longitude; used with `lat` to sort by distance (15km radius).'), ], ), ) class StoreViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet): """Customer-facing store browsing (home feed, store page).""" schema_tags = ['Stores'] permission_classes = [AllowAny] queryset = Store.objects.filter(status=Store.Status.APPROVED).select_related('category', 'city').distinct() filterset_class = StoreFilter def get_serializer_class(self): if self.action == 'retrieve': return StoreDetailSerializer return StoreListSerializer def get_queryset(self): queryset = super().get_queryset() lat = self.request.query_params.get('lat') lng = self.request.query_params.get('lng') if lat and lng: point = Point(float(lng), float(lat), srid=4326) queryset = queryset.filter(location__distance_lte=(point, D(km=15))).annotate( distance=Distance('location', point) ).order_by('distance') return queryset class SellerStoreView(APIView): """The authenticated seller's own store — GET/PATCH to manage it, POST to create it.""" schema_tags = ['Seller · Store'] serializer_class = SellerStoreSerializer permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements] required_alternate_scopes = { "GET": [[]], "POST": [[]], "PATCH": [[]], } def get(self, request): store = get_object_or_404(Store, owner=request.user) return Response(SellerStoreSerializer(store, context={'request': request}).data) def post(self, request): if hasattr(request.user, 'store'): return Response( {'detail': 'شما قبلاً یک فروشگاه ثبت کرده‌اید.'}, status=status.HTTP_409_CONFLICT, ) serializer = SellerStoreSerializer(data=request.data, context={'request': request}) serializer.is_valid(raise_exception=True) serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) @extend_schema(request=SellerStoreSerializer) def patch(self, request): store = get_object_or_404(Store, owner=request.user) serializer = SellerStoreSerializer( store, data=request.data, partial=True, context={'request': request}, ) serializer.is_valid(raise_exception=True) serializer.save() return Response(serializer.data) class SellerStoreWorkingHoursView(APIView): """Bulk get/set the authenticated seller's weekly working hours (S14).""" schema_tags = ['Seller · Store'] serializer_class = StoreWorkingHoursSerializer # permission_classes = [IsAuthenticated, IsStoreOwner] # TODO: permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements] required_alternate_scopes = { "GET": [[]], "PUT": [[]], } def get(self, request): hours = StoreWorkingHours.objects.filter(store=request.user.store) return Response(StoreWorkingHoursSerializer(hours, many=True).data) @extend_schema(request=StoreWorkingHoursSerializer(many=True)) def put(self, request): store = request.user.store entries = request.data if isinstance(request.data, list) else request.data.get('working_hours', []) serializer = StoreWorkingHoursSerializer(data=entries, many=True) serializer.is_valid(raise_exception=True) StoreWorkingHours.objects.filter(store=store).delete() StoreWorkingHours.objects.bulk_create( [StoreWorkingHours(store=store, **entry) for entry in serializer.validated_data] ) return Response(StoreWorkingHoursSerializer(store.working_hours.all(), many=True).data)