from django.conf import settings from django.contrib.auth import get_user_model from rest_framework import serializers from drf_spectacular.utils import extend_schema_field from apps.chat.models.chat import ChatRoom, ChatMessage from apps.profiles.models.institution import Institution User = get_user_model() class CentrifugoTokenResponseSerializer(serializers.Serializer): token = serializers.CharField(help_text="Signed HMAC-SHA256 JWT for Centrifugo WebSocket connection") ws_url = serializers.CharField(help_text="WebSocket endpoint URL for frontend client") user_id = serializers.IntegerField(help_text="Authenticated user ID") expires_in = serializers.IntegerField(help_text="Token lifetime in seconds") class ChatUserMiniSerializer(serializers.ModelSerializer): class Meta: model = User fields = ['id', 'email', 'fullname', 'avatar', 'user_type'] read_only_fields = fields class ChatInstitutionMiniSerializer(serializers.ModelSerializer): type_display = serializers.CharField(source='get_type_display', read_only=True) class Meta: model = Institution fields = ['id', 'name', 'slug', 'type', 'type_display', 'avatar', 'city', 'country'] read_only_fields = fields class ChatMessageSerializer(serializers.ModelSerializer): sender = ChatUserMiniSerializer(read_only=True) sender_institution = ChatInstitutionMiniSerializer(read_only=True) is_read_by_me = serializers.SerializerMethodField() read_by_count = serializers.SerializerMethodField() class Meta: model = ChatMessage fields = [ 'id', 'room', 'sender', 'sender_institution', 'content', 'attachment', 'attachment_name', 'is_read_by_me', 'read_by_count', 'created_at', ] read_only_fields = fields @extend_schema_field(serializers.BooleanField) def get_is_read_by_me(self, obj) -> bool: request = self.context.get('request') if not request or not request.user.is_authenticated: return False return obj.read_by.filter(id=request.user.id).exists() @extend_schema_field(serializers.IntegerField) def get_read_by_count(self, obj) -> int: return obj.read_by.count() class ChatMessageCreateSerializer(serializers.Serializer): content = serializers.CharField(required=False, allow_blank=True) attachment = serializers.FileField(required=False, allow_null=True) sender_institution_id = serializers.IntegerField(required=False, allow_null=True) def validate(self, attrs): if not attrs.get('content') and not attrs.get('attachment'): raise serializers.ValidationError("Message must contain either text content or an attachment.") return attrs class ChatRoomListSerializer(serializers.ModelSerializer): display_title = serializers.SerializerMethodField() participant_users = ChatUserMiniSerializer(many=True, read_only=True) participant_institutions = ChatInstitutionMiniSerializer(many=True, read_only=True) last_message = serializers.SerializerMethodField() unread_count = serializers.SerializerMethodField() centrifugo_channel = serializers.CharField(read_only=True) class Meta: model = ChatRoom fields = [ 'id', 'room_type', 'title', 'display_title', 'participant_users', 'participant_institutions', 'last_message', 'unread_count', 'centrifugo_channel', 'updated_at', ] read_only_fields = fields @extend_schema_field(serializers.CharField) def get_display_title(self, obj) -> str: request = self.context.get('request') user = request.user if request else None return obj.get_display_title(user) @extend_schema_field(ChatMessageSerializer(allow_null=True)) def get_last_message(self, obj): last_msg = obj.get_last_message() if not last_msg: return None return ChatMessageSerializer(last_msg, context=self.context).data @extend_schema_field(serializers.IntegerField) def get_unread_count(self, obj) -> int: request = self.context.get('request') if not request or not request.user.is_authenticated: return 0 return obj.get_unread_count(request.user) class ChatRoomDetailSerializer(serializers.ModelSerializer): display_title = serializers.SerializerMethodField() participant_users = ChatUserMiniSerializer(many=True, read_only=True) participant_institutions = ChatInstitutionMiniSerializer(many=True, read_only=True) centrifugo_channel = serializers.CharField(read_only=True) unread_count = serializers.SerializerMethodField() class Meta: model = ChatRoom fields = [ 'id', 'room_type', 'title', 'display_title', 'participant_users', 'participant_institutions', 'unread_count', 'centrifugo_channel', 'created_at', 'updated_at', ] read_only_fields = fields @extend_schema_field(serializers.CharField) def get_display_title(self, obj) -> str: request = self.context.get('request') user = request.user if request else None return obj.get_display_title(user) @extend_schema_field(serializers.IntegerField) def get_unread_count(self, obj) -> int: request = self.context.get('request') if not request or not request.user.is_authenticated: return 0 return obj.get_unread_count(request.user) class ChatRoomCreateSerializer(serializers.Serializer): room_type = serializers.ChoiceField( choices=ChatRoom.RoomType.choices, default=ChatRoom.RoomType.DIRECT ) title = serializers.CharField(required=False, allow_blank=True, max_length=255) target_user_id = serializers.IntegerField(required=False, allow_null=True) target_institution_id = serializers.IntegerField(required=False, allow_null=True) participant_user_ids = serializers.ListField( child=serializers.IntegerField(), required=False, default=list ) participant_institution_ids = serializers.ListField( child=serializers.IntegerField(), required=False, default=list ) initial_message = serializers.CharField(required=False, allow_blank=True)