from django.db import transaction from rest_framework import status from rest_framework.authentication import TokenAuthentication from rest_framework.authtoken.models import Token from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView from apps.support.models import SupportMessage, SupportRoom from apps.support.serializers import ( SendSupportMessageSerializer, SupportMessageSerializer, SupportRoomSerializer, ) def resolve_user_support_room(request, create_if_missing: bool = False): """ Resolves the SupportRoom for request.user (works for both logged-in and guest users). If a logged-in user does not yet have a room, checks the optional X-Guest-Token header to seamlessly migrate their existing guest support conversation. """ user = request.user room = SupportRoom.objects.filter(user=user).first() if room: return room # Check if user recently logged in from a guest session that already had a support chat guest_token_key = request.headers.get('X-Guest-Token', '').strip() if guest_token_key and not getattr(user, 'is_guest', False): guest_token_obj = ( Token.objects.select_related('user') .filter(key=guest_token_key) .first() ) if guest_token_obj and guest_token_obj.user_id != user.id: guest_room = SupportRoom.objects.filter(user=guest_token_obj.user).first() if guest_room: guest_room.user = user guest_room.save(update_fields=['user', 'updated_at']) return guest_room if create_if_missing: room, _ = SupportRoom.objects.get_or_create(user=user) return room return None class SupportChatMessagesView(APIView): """ GET /api/support/messages/?after_id= Returns the user's support room and messages (all or after `after_id` for short polling). POST /api/support/messages/ Sends a new message from the user (or guest user) to support. """ authentication_classes = [TokenAuthentication] permission_classes = [IsAuthenticated] def get(self, request, *args, **kwargs): room = resolve_user_support_room(request, create_if_missing=False) if not room: return Response( { 'room': None, 'messages': [], }, status=status.HTTP_200_OK, ) after_id_param = request.query_params.get('after_id') messages_qs = room.messages.select_related('sender').order_by('id') if after_id_param is not None: try: after_id = int(after_id_param) if after_id > 0: messages_qs = messages_qs.filter(id__gt=after_id) except (TypeError, ValueError): pass messages_list = list(messages_qs) # Mark support replies as read when user fetches messages in open chat mark_read = request.query_params.get('mark_read', 'true').lower() != 'false' if mark_read and (room.unread_by_user > 0 or any( m.sender_type == SupportMessage.SenderType.SUPPORT and not m.is_read for m in messages_list )): SupportMessage.objects.filter( room=room, sender_type=SupportMessage.SenderType.SUPPORT, is_read=False, ).update(is_read=True) if room.unread_by_user != 0: room.unread_by_user = 0 room.save(update_fields=['unread_by_user', 'updated_at']) return Response( { 'room': SupportRoomSerializer(room).data, 'messages': SupportMessageSerializer(messages_list, many=True).data, }, status=status.HTTP_200_OK, ) @transaction.atomic def post(self, request, *args, **kwargs): serializer = SendSupportMessageSerializer(data=request.data) serializer.is_valid(raise_exception=True) room = resolve_user_support_room(request, create_if_missing=True) message = SupportMessage.objects.create( room=room, sender_type=SupportMessage.SenderType.USER, sender=request.user, content=serializer.validated_data['content'], ) room.refresh_from_db() return Response( { 'room': SupportRoomSerializer(room).data, 'message': SupportMessageSerializer(message).data, }, status=status.HTTP_201_CREATED, ) class SupportUnreadCountView(APIView): """ GET /api/support/unread/ Lightweight endpoint to check if the user has unread support specialist replies. """ authentication_classes = [TokenAuthentication] permission_classes = [IsAuthenticated] def get(self, request, *args, **kwargs): room = resolve_user_support_room(request, create_if_missing=False) return Response( { 'unread_count': room.unread_by_user if room else 0, 'status': room.status if room else None, }, status=status.HTTP_200_OK, )