"""
Chat serializers for conversations and messages.
"""
from rest_framework import serializers
from django.contrib.auth import get_user_model
from chat.models import (
    Conversation, Message, MessageReaction, ConversationVisibility,
    ChatAppearance, ConversationMember, GroupJoinRequest, MessageRead
)
from users.serializers import UserSerializer
from categories.models import Category
from posts.views import get_visible_posts

User = get_user_model()


class MessageReactionSerializer(serializers.ModelSerializer):
    """Serializer for message reactions."""
    user = UserSerializer(read_only=True)

    class Meta:
        model = MessageReaction
        fields = ['id', 'user', 'emoji', 'created_at']
        read_only_fields = ['id', 'user', 'created_at']


class SharedCategorySerializer(serializers.ModelSerializer):
    created_by = UserSerializer(read_only=True)

    class Meta:
        model = Category
        fields = ['id', 'title', 'description', 'created_by', 'shared_count']

class SharedProfileSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'username', 'first_name', 'last_name', 'avatar', 'bio']

class SharedPostSerializer(serializers.Serializer):
    """Serializer for shared posts in chat messages."""
    id = serializers.IntegerField()
    content = serializers.SerializerMethodField()
    author = serializers.SerializerMethodField()
    first_image = serializers.SerializerMethodField()
    likes_count = serializers.SerializerMethodField()
    comments_count = serializers.SerializerMethodField()
    shared_count = serializers.SerializerMethodField()
    created_at = serializers.DateTimeField()
    # Include flags to help frontend
    show_likes_count = serializers.BooleanField()
    show_comments = serializers.BooleanField()
    show_shares_count = serializers.BooleanField()
    allow_likes = serializers.BooleanField()
    allow_comments = serializers.BooleanField()
    allow_sharing = serializers.BooleanField()

    def get_content(self, obj):
        if obj.content:
            return obj.content[:150] + ('...' if len(obj.content) > 150 else '')
        return ''

    def get_author(self, obj):
        if obj.created_by:
            return {
                'id': obj.created_by.id,
                'username': obj.created_by.username,
                'first_name': obj.created_by.first_name,
                'last_name': obj.created_by.last_name,
                'avatar': obj.created_by.avatar.url if obj.created_by.avatar else None
            }
        return None

    def get_first_image(self, obj):
        first_image = obj.postimage_set.order_by('order').first()
        if first_image and first_image.image:
            return first_image.image.image.url if first_image.image.image else None
        return None

    def get_likes_count(self, obj):
        if not obj.show_likes_count:
            return None
        from likes.models import LikePost
        return LikePost.objects.filter(post=obj).count()

    def get_comments_count(self, obj):
        if not obj.show_comments:
            return None
        from comments.models import Comment
        return Comment.objects.filter(post=obj, is_active=True).count()

    def get_shared_count(self, obj):
        if not obj.show_shares_count:
            return None
        return obj.shared_count


class MessageSerializer(serializers.ModelSerializer):
    """Serializer for reading messages."""
    sender = UserSerializer(read_only=True)
    reactions = MessageReactionSerializer(many=True, read_only=True)
    conversation_id = serializers.CharField(read_only=True)
    attachment_type = serializers.ReadOnlyField()
    reactions_count = serializers.SerializerMethodField()
    reply_to_preview = serializers.SerializerMethodField()
    read_by_users = serializers.SerializerMethodField()
    shared_post = serializers.SerializerMethodField()
    shared_category = serializers.SerializerMethodField()
    shared_profile = serializers.SerializerMethodField()

    class Meta:
        model = Message
        fields = [
            'id', 'conversation_id', 'sender', 'content', 'message_type', 'attachment',
            'attachment_type', 'is_read', 'is_edited', 'is_deleted', 'reply_to',
            'reply_to_preview', 'reactions', 'reactions_count', 'created_at',
            'sender_name_snapshot', 'sender_avatar_snapshot', 'read_by_users',
            'shared_post', 'shared_category', 'shared_profile'
        ]
        read_only_fields = ['id', 'conversation', 'sender', 'created_at', 'sender_name_snapshot', 'sender_avatar_snapshot', 'message_type']

    def to_representation(self, instance):
        data = super().to_representation(instance)
        if not instance.sender:
            data['sender'] = {
                'id': None,
                'username': "Deleted Account",
                'first_name': "Deleted",
                'last_name': "Account",
                'avatar': instance.sender_avatar_snapshot or "/default_user_avatar.png",
                'display_name': instance.sender_name_snapshot or "Deleted Account"
            }
        elif 'sender' in data and data['sender']:
             # Add display name to active user data for consistency
             data['sender']['display_name'] = instance.sender_name_snapshot or f"{instance.sender.first_name} {instance.sender.last_name}".strip()

        return data

    def get_read_by_users(self, obj):
        """Get list of users who have read the message."""
        # This will rely on prefetch_related in the viewset to be efficient
        return [
            {
                'id': read.user.id,
                'username': read.user.username,
                'first_name': read.user.first_name,
                'last_name': read.user.last_name,
                'display_name': f"{read.user.first_name} {read.user.last_name}".strip() or read.user.username,
                'avatar': read.user.avatar.url if read.user.avatar else None,
                'read_at': read.read_at
            }
            for read in obj.read_by.all()
        ]



    def get_reactions_count(self, obj):
        """Get count of reactions grouped by emoji."""
        # Manual count since annotate with serializers.IntegerField doesn't work
        from django.db.models import Count
        reactions = obj.reactions.values('emoji').annotate(count=Count('id'))
        return {r['emoji']: r['count'] for r in reactions}

    def get_reply_to_preview(self, obj):
        """Get preview of the message being replied to."""
        if obj.reply_to:
            if obj.reply_to.is_deleted:
                return None

            if obj.reply_to.content:
                content_preview = obj.reply_to.content[:100]
            else:
                content_preview = "Pièce jointe"

            return {
                'id': obj.reply_to.id,
                'sender': {
                    'id': obj.reply_to.sender_id,
                    'username': obj.reply_to.sender.username if obj.reply_to.sender else "Deleted Account",
                    'avatar': obj.reply_to.sender.avatar.url if obj.reply_to.sender and obj.reply_to.sender.avatar else (obj.reply_to.sender_avatar_snapshot or "/default_user_avatar.png"),
                    'display_name': obj.reply_to.sender_name_snapshot or (f"{obj.reply_to.sender.first_name} {obj.reply_to.sender.last_name}".strip() if obj.reply_to.sender else "Deleted Account")
                },
                'content': content_preview,
                'is_deleted': obj.reply_to.is_deleted
            }
        return None

    def get_shared_post(self, obj):
        """Get shared post data if this is a post message."""
        if obj.message_type == 'post':
            if not obj.shared_post or not obj.shared_post.is_active:
                return {'is_available': False}

            user = self.context.get('user')
            if not user:
                request = self.context.get('request')
                if request:
                    user = request.user

            if user and user.is_authenticated:
                # Check if the post is visible to the current user
                # We use get_visible_posts to ensure audience and blocking rules are respected
                visible_posts = get_visible_posts(user)
                if not visible_posts.filter(id=obj.shared_post.id).exists():
                    return {'is_available': False}

            data = SharedPostSerializer(obj.shared_post, context=self.context).data
            data['is_available'] = True
            return data
        return None

    def get_shared_category(self, obj):
        """Get shared category data if this is a category message."""
        if obj.message_type == 'category':
            if not obj.shared_category:
                return {'is_available': False}

            data = SharedCategorySerializer(obj.shared_category, context=self.context).data
            data['is_available'] = True
            return data
        return None

    def get_shared_profile(self, obj):
        """Get shared profile data if this is a profile message."""
        if obj.message_type == 'profile' and obj.shared_profile:
            return SharedProfileSerializer(obj.shared_profile).data
        return None


class MessageCreateSerializer(serializers.ModelSerializer):
    """Serializer for creating messages."""
    shared_post_id = serializers.IntegerField(required=False, write_only=True)

    class Meta:
        model = Message
        fields = ['content', 'attachment', 'reply_to', 'shared_post_id']

    def validate_shared_post_id(self, value):
        from posts.models import Post
        if value:
            try:
                post = Post.objects.get(id=value, is_active=True)
                if post.audience == 'ONLY_ME':
                    raise serializers.ValidationError("Ce post est privé et ne peut pas être partagé.")
            except Post.DoesNotExist:
                raise serializers.ValidationError("Post not found or inactive.")
        return value

    def validate(self, attrs):
        content = attrs.get('content', '').strip()
        attachment = attrs.get('attachment')
        shared_post_id = attrs.get('shared_post_id')

        if not content and not attachment and not shared_post_id:
            raise serializers.ValidationError(
                "Le message doit contenir du texte, une pièce jointe ou un post partagé."
            )
        return attrs


class ConversationSerializer(serializers.ModelSerializer):
    """Serializer for reading conversations (both 1-on-1 and groups)."""
    participants = UserSerializer(many=True, read_only=True)
    last_message = serializers.SerializerMethodField()
    unread_count = serializers.SerializerMethodField()
    other_participant = serializers.SerializerMethodField()
    is_accepted = serializers.SerializerMethodField()
    is_muted = serializers.SerializerMethodField()
    is_visible = serializers.SerializerMethodField()
    initiator_id = serializers.ReadOnlyField(source='initiator.id')
    # Group chat fields
    admin_id = serializers.ReadOnlyField(source='admin.id')
    admins = serializers.SerializerMethodField()
    is_admin = serializers.SerializerMethodField()
    display_name = serializers.SerializerMethodField()
    group_image = serializers.SerializerMethodField()
    member_count = serializers.SerializerMethodField()

    class Meta:
        model = Conversation
        fields = [
            'id', 'participants', 'other_participant', 'last_message',
            'unread_count', 'is_accepted', 'is_muted', 'is_visible',
            'initiator_id', 'created_at', 'updated_at',
            # Group fields
            'is_group', 'name', 'admin_id', 'admins', 'is_admin', 'display_name', 'group_image', 'member_count',
            # Invite fields
            'invite_enabled', 'invite_require_approval', 'invite_token', 'invite_short_token', 'invite_expires_at'
        ]
        read_only_fields = ['id', 'created_at', 'updated_at']

    def get_admins(self, obj):
        """Get list of admin IDs."""
        if obj.is_group:
            # Include both the legacy admin and new admins list
            admin_ids = set(obj.admins.values_list('id', flat=True))
            if obj.admin_id:
                admin_ids.add(obj.admin_id)
            return list(admin_ids)
        return []

    def get_is_admin(self, obj):
        """Check if current user is an admin."""
        request = self.context.get('request')
        if request and request.user.is_authenticated and obj.is_group:
            if obj.admin_id == request.user.id:
                return True
            return obj.admins.filter(id=request.user.id).exists()
        return False

    def get_display_name(self, obj):
        """Get display name for the conversation."""
        if obj.is_group:
            return obj.get_display_name()
        return None

    def get_group_image(self, obj):
        """Get group image URL."""
        if obj.is_group:
            return obj.get_image_url()
        return None

    def get_member_count(self, obj):
        """Get member count for groups."""
        if obj.is_group:
            return obj.participants.count()
        return None

    def get_is_muted(self, obj):
        """Check if conversation is muted for current user."""
        request = self.context.get('request')
        if request and request.user.is_authenticated:
            visibility = obj.visibilities.filter(user=request.user).first()
            return visibility.is_muted if visibility else False
        return False

    def get_is_visible(self, obj):
        """Check if conversation is visible for current user."""
        request = self.context.get('request')
        if request and request.user.is_authenticated:
            visibility = obj.visibilities.filter(user=request.user).first()
            return visibility.is_visible if visibility else True
        return True

    def get_last_message(self, obj):
        """Get the most recent message in the conversation, respecting visibility."""
        request = self.context.get('request')
        hidden_until = None

        if request and request.user.is_authenticated:
            visibility = obj.visibilities.filter(user=request.user).first()
            if visibility:
                hidden_until = visibility.hidden_until

        messages = obj.messages.filter(is_deleted=False)

        if hidden_until:
            messages = messages.filter(created_at__gt=hidden_until)

        last_msg = messages.order_by('-created_at').first()

        if last_msg:
            # For system messages (JSON), send full content. For text, truncate.
            content = last_msg.content
            if (last_msg.message_type or 'text') == 'text' and content:
                content = content[:100]

            return {
                'id': last_msg.id,
                'sender_id': last_msg.sender_id,
                'content': content,
                'has_attachment': bool(last_msg.attachment),
                'attachment_type': last_msg.attachment_type,
                'message_type': last_msg.message_type or 'text',
                'created_at': last_msg.created_at,
                'is_read': last_msg.read_by.filter(user=request.user).exists() if (request and request.user.is_authenticated) else last_msg.is_read
            }
        return None

    def get_unread_count(self, obj):
        """Get count of unread messages for current user."""
        request = self.context.get('request')
        if request and request.user.is_authenticated:
            # Count messages NOT sent by user and NOT read by user
            return obj.messages.filter(
                is_deleted=False
            ).exclude(
                sender=request.user
            ).exclude(
                read_by__user=request.user
            ).count()
        return 0

    def get_other_participant(self, obj):
        """Get the other participant in the conversation (not current user)."""
        request = self.context.get('request')
        if request and request.user.is_authenticated:
            other = obj.participants.exclude(id=request.user.id).first()
            if other:
                return UserSerializer(other).data

            # If no other participant found, it means they might have deleted their account
            return {
                'id': None,
                'username': "Deleted Account",
                'first_name': "Deleted",
                'last_name': "Account",
                'display_name': "Deleted Account",
                'avatar': "/default_user_avatar.png"
            }
        return None

    def get_is_accepted(self, obj):
        """Check if conversation is accepted."""
        request = self.context.get('request')
        if not request or not request.user.is_authenticated:
            return obj.is_accepted

        if obj.is_group:
            # For groups, acceptance means being a full participant
            # If they are in participants list, they accepted.
            # If they have a pending request, they haven't.
            return obj.participants.filter(id=request.user.id).exists()

        return obj.is_accepted

    def to_representation(self, instance):
        data = super().to_representation(instance)
        request = self.context.get('request')

        # Only admin should see the invite tokens
        is_admin = False
        if request and request.user.is_authenticated:
             if instance.is_group and instance.admin_id == request.user.id:
                 is_admin = True

        if not is_admin:
            # Check if user is a participant to allow sharing the link
            is_participant = False
            if request and request.user.is_authenticated:
                # We can check participation from the data itself or the instance
                # Using instance.participants is reliable but watch out for N+1 if not prefetched
                # However, for a single detail view it's fine.
                if instance.is_group and instance.participants.filter(id=request.user.id).exists():
                    is_participant = True

            if not is_participant:
                data.pop('invite_token', None)
                data.pop('invite_short_token', None)

        return data


class ConversationCreateSerializer(serializers.Serializer):
    """Serializer for starting a new conversation."""
    participant_id = serializers.IntegerField(write_only=True)
    initial_message = serializers.CharField(required=False, allow_blank=True)
    initial_attachment = serializers.FileField(required=False, allow_null=True)

    def validate_participant_id(self, value):
        try:
            User.objects.get(id=value)
        except User.DoesNotExist:
            raise serializers.ValidationError("User not found.")
        return value

    def validate(self, attrs):
        from chat.services import can_message_user

        request = self.context.get('request')
        if not request:
            raise serializers.ValidationError("Request context required.")

        sender = request.user
        recipient_id = attrs['participant_id']
        recipient = User.objects.get(id=recipient_id)

        can_message, error = can_message_user(sender, recipient)
        if not can_message:
            raise serializers.ValidationError(error)

        attrs['recipient'] = recipient
        return attrs

    def create(self, validated_data):
        from chat.services import get_or_create_conversation

        request = self.context.get('request')
        sender = request.user
        recipient = validated_data['recipient']
        initial_message = validated_data.get('initial_message', '').strip()
        initial_attachment = validated_data.get('initial_attachment')

        # Try to find existing conversation first
        conversation, created = get_or_create_conversation(sender, recipient, create_if_not_exists=False)

        if not conversation:
            # No existing conversation. Create one.
            # We allow empty creation to support flows like 'Share to new user' where the message comes immediately after.

            # Create the new conversation as an invitation
            conversation, created = get_or_create_conversation(sender, recipient, create_if_not_exists=True)

        # Create initial message if provided
        if initial_message or initial_attachment:
            Message.objects.create(
                conversation=conversation,
                sender=sender,
                sender_name_snapshot=f"{sender.first_name} {sender.last_name}".strip() or sender.username,
                sender_avatar_snapshot=sender.avatar.url if sender.avatar else None,
                content=initial_message,
                attachment=initial_attachment
            )
            # Ensure everyone sees the conversation
            from chat.services import ensure_visibility_on_new_message
            ensure_visibility_on_new_message(conversation, sender)

            # Update conversation timestamp and save
            conversation.save()

        return conversation


class ReactionCreateSerializer(serializers.Serializer):
    """Serializer for adding a reaction to a message."""
    emoji = serializers.CharField(max_length=10)

    def validate_emoji(self, value):
        if not value.strip():
            raise serializers.ValidationError("L'emoji ne peut pas être vide.")
        return value.strip()


class ChatAppearanceSerializer(serializers.ModelSerializer):
    """Serializer for chat appearance customization."""

    class Meta:
        model = ChatAppearance
        fields = [
            'id', 'bg_type', 'bg_color', 'bg_gradient_start', 'bg_gradient_end',
            'bg_template', 'bg_image', 'bg_image_position', 'message_bg_sent',
            'message_bg_received', 'message_text_sent', 'message_text_received',
            'display_mode', 'created_at', 'updated_at'
        ]
        read_only_fields = ['id', 'created_at', 'updated_at']

    def validate_bg_color(self, value):
        """Validate hex color format."""
        if value and not value.startswith('#'):
            raise serializers.ValidationError("La couleur doit être au format hexadécimal (#RRGGBB).")
        if value and len(value) != 7:
            raise serializers.ValidationError("La couleur doit être au format hexadécimal (#RRGGBB).")
        return value

    def validate_bg_gradient_start(self, value):
        if value and (not value.startswith('#') or len(value) != 7):
            raise serializers.ValidationError("La couleur doit être au format hexadécimal (#RRGGBB).")
        return value

    def validate_bg_gradient_end(self, value):
        if value and (not value.startswith('#') or len(value) != 7):
            raise serializers.ValidationError("La couleur doit être au format hexadécimal (#RRGGBB).")
        return value

    def validate(self, data):
        """Ensure gradient has both start and end colors."""
        if data.get('bg_type') == 'gradient':
            if not data.get('bg_gradient_start') or not data.get('bg_gradient_end'):
                raise serializers.ValidationError({
                    "bg_gradient": "Un dégradé nécessite à la fois une couleur de début et de fin."
                })
        return data


# ==================== Group Chat Serializers ====================

class ConversationMemberSerializer(serializers.ModelSerializer):
    """Serializer for group conversation members."""
    user = UserSerializer(read_only=True)

    class Meta:
        model = ConversationMember
        fields = ['id', 'user', 'is_muted', 'is_hidden', 'joined_at']
        read_only_fields = ['id', 'joined_at']


class GroupJoinRequestSerializer(serializers.ModelSerializer):
    """Serializer for group join requests."""
    user = UserSerializer(read_only=True)
    requested_by = UserSerializer(read_only=True)

    class Meta:
        model = GroupJoinRequest
        fields = ['id', 'user', 'requested_by', 'status', 'created_at', 'updated_at']
        read_only_fields = ['id', 'created_at', 'updated_at']


class MessageReadSerializer(serializers.ModelSerializer):
    """Serializer for message read receipts."""
    user = UserSerializer(read_only=True)

    class Meta:
        model = MessageRead
        fields = ['id', 'user', 'read_at']
        read_only_fields = ['id', 'read_at']


class GroupConversationCreateSerializer(serializers.Serializer):
    """Serializer for creating a group conversation."""
    name = serializers.CharField(max_length=255, required=False, allow_blank=True)
    image = serializers.ImageField(required=False, allow_null=True)
    member_ids = serializers.ListField(
        child=serializers.IntegerField(),
        min_length=1,
        max_length=9,  # Max 9 other members + creator = 10
        write_only=True
    )
    initial_message = serializers.CharField(required=False, allow_blank=True)

    def validate_member_ids(self, value):
        """Validate member IDs exist and limit to unique users."""
        unique_ids = list(set(value))
        if len(unique_ids) > 9:
            raise serializers.ValidationError("Maximum 9 members can be added (10 including yourself).")

        # Verify all users exist
        existing_users = User.objects.filter(id__in=unique_ids)
        if existing_users.count() != len(unique_ids):
            raise serializers.ValidationError("One or more users not found.")

        return unique_ids

    def validate(self, attrs):
        request = self.context.get('request')
        if not request:
            raise serializers.ValidationError("Request context required.")

        # Creator cannot be in the member list
        member_ids = attrs.get('member_ids', [])
        if request.user.id in member_ids:
            # Silently remove creator from member list
            member_ids = [mid for mid in member_ids if mid != request.user.id]
            attrs['member_ids'] = member_ids

        return attrs

    def create(self, validated_data):
        request = self.context.get('request')
        creator = request.user
        member_ids = validated_data.get('member_ids', [])
        name = validated_data.get('name', '').strip()
        image = validated_data.get('image')
        initial_message = validated_data.get('initial_message', '').strip()

        # Create the conversation
        conversation = Conversation.objects.create(
            is_group=True,
            name=name,
            image=image,
            admin=creator,
            initiator=creator,
            is_accepted=True  # Groups are automatically accepted
        )

        # Add creator as participant
        conversation.participants.add(creator)

        # Add creator as admin
        conversation.admins.add(creator)

        # Add members AUTOMATICALLY
        members = User.objects.filter(id__in=member_ids)
        for member in members:
            # Add to participants
            conversation.participants.add(member)

            # Create Member record
            ConversationMember.objects.create(
                conversation=conversation,
                user=member
            )

            # Create Visibility record
            ConversationVisibility.objects.get_or_create(
                user=member,
                conversation=conversation,
                defaults={'is_visible': True}
            )

            # Broadcast to NEW MEMBER
            from channels.layers import get_channel_layer
            from asgiref.sync import async_to_sync
            channel_layer = get_channel_layer()

            if channel_layer:
                 # We can send 'group_user_added' so it appears in their list
                async_to_sync(channel_layer.group_send)(
                    f"user_{member.id}",
                    {
                        "type": "group_user_added",
                        "conversation_id": str(conversation.id),
                        "user": {
                            "id": member.id,
                            "username": member.username,
                            "avatar": member.avatar.url if member.avatar else None
                        }
                    }
                )

        # Create ConversationMember for creator too
        ConversationMember.objects.create(
            conversation=conversation,
            user=creator
        )

        # Create ConversationVisibility for all participants (creator + members)
        for participant in conversation.participants.all():
            ConversationVisibility.objects.get_or_create(
                user=participant,
                conversation=conversation,
                defaults={'is_visible': True}
            )

        # Create initial system message or user message
        if initial_message:
            Message.objects.create(
                conversation=conversation,
                sender=creator,
                sender_name_snapshot=f"{creator.first_name} {creator.last_name}".strip() or creator.username,
                sender_avatar_snapshot=creator.avatar.url if creator.avatar else None,
                content=initial_message
            )

        # Update timestamp
        conversation.save()

        return conversation


class GroupUpdateSerializer(serializers.ModelSerializer):
    """Serializer for updating group conversation settings."""

    class Meta:
        model = Conversation
        fields = ['name', 'image', 'invite_enabled', 'invite_require_approval', 'invite_expires_at']

    def validate(self, attrs):
        # Ensure only admin can update (done in view, but double-check)
        request = self.context.get('request')
        if request and self.instance:
            # Check if user is creator OR in admins list
            is_creator = self.instance.admin_id == request.user.id
            is_promoted_admin = self.instance.admins.filter(id=request.user.id).exists()

            if not (is_creator or is_promoted_admin):
                raise serializers.ValidationError("Only group admins can update settings.")
        return attrs


class GroupInviteLinkSerializer(serializers.ModelSerializer):
    """Serializer for group join info via link."""
    member_count = serializers.SerializerMethodField()
    admin_name = serializers.CharField(source='admin.username', read_only=True)

    class Meta:
        model = Conversation
        fields = ['id', 'name', 'image', 'member_count', 'invite_require_approval', 'admin_name']
        read_only_fields = ['id', 'name', 'image', 'member_count', 'invite_require_approval', 'admin_name']

    def get_member_count(self, obj):
        return obj.participants.count()
