from rest_framework import serializers
from posts.models import Post
from users.serializers import UserSerializer
from categories.serializers import CategorySerializer
from images.serializers import ImageSerializer
from videos.serializers import VideoSerializer


class PostSerializer(serializers.ModelSerializer):
    created_by = UserSerializer(read_only=True)
    category = CategorySerializer(read_only=True)
    images = serializers.SerializerMethodField()
    videos = serializers.SerializerMethodField()
    likes_count = serializers.SerializerMethodField()
    comments_count = serializers.SerializerMethodField()
    is_liked = serializers.SerializerMethodField()
    is_saved = serializers.SerializerMethodField()
    latest_comment = serializers.SerializerMethodField()

    class Meta:
        model = Post
        fields = ['id', 'content', 'audience', 'created_at', 'updated_at', 'is_updated', 'is_active',
                  'created_by', 'category', 'images', 'videos', 'likes_count', 'comments_count', 'shared_count', 'is_liked', 'is_saved', 'latest_comment',
                  'allow_likes', 'allow_comments', 'allow_sharing', 'show_comments', 'show_likes_count', 'show_shares_count', 'allow_saving']
        read_only_fields = ['id', 'created_at', 'updated_at', 'is_updated', 'created_by']

    def get_images(self, obj):
        # Fetch images through the through model to ensure preserved order
        post_images = obj.postimage_set.all().select_related('image').order_by('order')
        ordered_images = [pi.image for pi in post_images]
        return ImageSerializer(ordered_images, many=True, context=self.context).data

    def get_videos(self, obj):
        # Fetch videos through the through model to ensure preserved order
        post_videos = obj.postvideo_set.all().select_related('video').order_by('order')
        ordered_videos = [pv.video for pv in post_videos]
        return VideoSerializer(ordered_videos, many=True, context=self.context).data

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

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

    def get_is_liked(self, obj):
        from likes.models import LikePost
        request = self.context.get('request')
        if request and request.user.is_authenticated:
            return LikePost.objects.filter(user=request.user, post=obj).exists()
        return False

    def get_is_saved(self, obj):
        from saved.models import SavedPost
        request = self.context.get('request')
        if request and request.user.is_authenticated:
            return SavedPost.objects.filter(user=request.user, post=obj).exists()
        return False

    def get_latest_comment(self, obj):
        from comments.models import Comment
        from comments.serializers import CommentSerializer
        comment = Comment.objects.filter(post=obj, is_active=True).first()
        if comment:
            return CommentSerializer(comment, context=self.context).data
        return None


class PostCreateSerializer(serializers.ModelSerializer):
    images = serializers.ListField(
        child=serializers.ImageField(),
        write_only=True,
        required=False,
        allow_empty=True
    )
    remove_images = serializers.ListField(
        child=serializers.IntegerField(),
        write_only=True,
        required=False
    )
    image_order = serializers.ListField(
        child=serializers.CharField(),
        write_only=True,
        required=False
    )
    # Video support
    videos = serializers.ListField(
        child=serializers.FileField(),
        write_only=True,
        required=False,
        allow_empty=True
    )
    remove_videos = serializers.ListField(
        child=serializers.IntegerField(),
        write_only=True,
        required=False
    )
    video_order = serializers.ListField(
        child=serializers.CharField(),
        write_only=True,
        required=False
    )

    allow_likes = serializers.BooleanField(required=False, default=True)
    allow_comments = serializers.BooleanField(required=False, default=True)
    allow_sharing = serializers.BooleanField(required=False, default=True)
    show_comments = serializers.BooleanField(required=False, default=True)
    show_likes_count = serializers.BooleanField(required=False, default=True)
    show_shares_count = serializers.BooleanField(required=False, default=True)
    allow_saving = serializers.BooleanField(required=False, default=True)

    class Meta:
        model = Post
        fields = ['content', 'audience', 'category', 'images', 'remove_images', 'image_order',
                  'videos', 'remove_videos', 'video_order',
                  'allow_likes', 'allow_comments', 'allow_sharing', 'show_comments', 'show_likes_count', 'show_shares_count', 'allow_saving']

    def validate(self, data):
        """Validation globale pour l'audience et présence de contenu/images"""
        request = self.context.get('request')
        user = request.user if request else None

        content = data.get('content')
        if content is None and self.instance:
             content = self.instance.content

        if content is None:
             content = ''
        content = content.strip()
        images = data.get('images', [])
        videos = data.get('videos', [])

        # Check if we have at least content or images or videos
        # In case of update, we might have existing media not being removed
        existing_images_count = 0
        existing_videos_count = 0
        if self.instance:
            remove_images = data.get('remove_images', [])
            existing_images_count = self.instance.images.exclude(id__in=remove_images).count()

            remove_videos = data.get('remove_videos', [])
            existing_videos_count = self.instance.videos.exclude(id__in=remove_videos).count()

        # Check for files in request.FILES
        has_uploaded_media = False
        if request and hasattr(request, 'FILES'):
            has_uploaded_images = bool(request.FILES.getlist('images')) or any(
                key.startswith('image_new_') for key in request.FILES.keys()
            )
            has_uploaded_videos = bool(request.FILES.getlist('videos')) or any(
                key.startswith('video_new_') for key in request.FILES.keys()
            )
            has_uploaded_media = has_uploaded_images or has_uploaded_videos

        if not content and not images and not videos and not has_uploaded_media and existing_images_count == 0 and existing_videos_count == 0:
            raise serializers.ValidationError("Un post doit avoir du contenu ou au moins une image ou video.")

        if user and user.is_authenticated and user.is_private:
            audience = data.get('audience', self.instance.audience if self.instance else 'PUBLIC')
            if audience == 'PUBLIC':
                raise serializers.ValidationError({"audience": "Les comptes privés ne peuvent pas publier de posts publics."})

        return data

    def create(self, validated_data):
        request = self.context.get('request')
        image_order = request.data.getlist('image_order')

        validated_data.pop('images', [])
        validated_data.pop('remove_images', None)
        validated_data.pop('image_order', None)
        validated_data.pop('videos', [])
        validated_data.pop('remove_videos', None)
        validated_data.pop('video_order', None)

        if not request or not hasattr(request, 'user'):
            raise serializers.ValidationError("User authentication required")

        post = Post.objects.create(created_by=request.user, **validated_data)

        from images.models import Image
        from videos.models import Video
        from posts.models import PostImage, PostVideo

        # Create images and map them to their placeholders
        # We expect files to be named 'image_new_0', 'image_new_1', etc.
        # FALLBACK: if not found by key, we'll try to use the regular 'images' list in order
        new_images_by_key = {}
        for key in request.FILES:
            if key.startswith('image_new_'):
                img = Image.objects.create(
                    image=request.FILES[key],
                    created_by=request.user
                )
                new_images_by_key[key.replace('image_', '')] = img

        # Fallback for old/standard 'images' list if no unique keys used
        standard_images = request.FILES.getlist('images')
        standard_new_images = []
        if not new_images_by_key and standard_images:
            for image_file in standard_images:
                img = Image.objects.create(image=image_file, created_by=request.user)
                standard_new_images.append(img)

        if image_order:
            for index, item in enumerate(image_order):
                img = None
                if item.startswith('new_'):
                    if item in new_images_by_key:
                        img = new_images_by_key[item]
                    else:
                        try:
                            new_idx = int(item.split('_')[1])
                            if new_idx < len(standard_new_images):
                                img = standard_new_images[new_idx]
                        except (ValueError, IndexError):
                            pass

                if img:
                    PostImage.objects.create(post=post, image=img, order=index)
        else:
            # Fallback if no order: use all created images
            all_imgs = list(new_images_by_key.values()) + standard_new_images
            for index, img in enumerate(all_imgs):
                PostImage.objects.create(post=post, image=img, order=index)

        # Create videos and map them
        new_videos_by_key = {}
        for key in request.FILES:
            if key.startswith('video_new_'):
                vid = Video.objects.create(file=request.FILES[key], created_by=request.user)
                new_videos_by_key[key.replace('video_', '')] = vid

        standard_videos = request.FILES.getlist('videos')
        standard_new_videos = []
        if not new_videos_by_key and standard_videos:
            for video_file in standard_videos:
                vid = Video.objects.create(file=video_file, created_by=request.user)
                standard_new_videos.append(vid)

        video_order = request.data.getlist('video_order')
        if video_order:
            for index, item in enumerate(video_order):
                vid = None
                if item.startswith('new_'):
                    if item in new_videos_by_key:
                        vid = new_videos_by_key[item]
                    else:
                        try:
                            new_idx = int(item.split('_')[1])
                            if new_idx < len(standard_new_videos):
                                vid = standard_new_videos[new_idx]
                        except (ValueError, IndexError):
                            pass
                if vid:
                    PostVideo.objects.create(post=post, video=vid, order=index)
        else:
            all_vids = list(new_videos_by_key.values()) + standard_new_videos
            for index, vid in enumerate(all_vids):
                PostVideo.objects.create(post=post, video=vid, order=index)

        return post

    def update(self, instance, validated_data):
        request = self.context.get('request')
        remove_image_ids = request.data.getlist('remove_images')
        image_order = request.data.getlist('image_order')

        validated_data.pop('images', [])
        validated_data.pop('remove_images', [])
        validated_data.pop('image_order', [])
        validated_data.pop('videos', [])
        validated_data.pop('remove_videos', [])
        validated_data.pop('video_order', [])

        if not request or not hasattr(request, 'user'):
            raise serializers.ValidationError("User authentication required")

        for attr, value in validated_data.items():
            setattr(instance, attr, value)

        if remove_image_ids:
            instance.images.filter(id__in=remove_image_ids).delete()

        remove_video_ids = request.data.getlist('remove_videos')
        if remove_video_ids:
            instance.videos.filter(id__in=remove_video_ids).delete()

        instance.is_updated = True
        instance.save()

        from images.models import Image
        from videos.models import Video
        from posts.models import PostImage, PostVideo

        # Create new images with unique keys support
        new_images_by_key = {}
        for key in request.FILES:
            if key.startswith('image_new_'):
                img = Image.objects.create(image=request.FILES[key], created_by=request.user)
                new_images_by_key[key.replace('image_', '')] = img

        standard_images = request.FILES.getlist('images')
        standard_new_images = []
        if not new_images_by_key and standard_images:
            for image_file in standard_images:
                img = Image.objects.create(image=image_file, created_by=request.user)
                standard_new_images.append(img)

        if image_order:
            instance.postimage_set.all().delete()
            for index, item in enumerate(image_order):
                img = None
                if item.startswith('new_'):
                    if item in new_images_by_key:
                        img = new_images_by_key[item]
                    else:
                        try:
                            new_idx = int(item.split('_')[1])
                            if new_idx < len(standard_new_images):
                                img = standard_new_images[new_idx]
                        except (ValueError, IndexError):
                            pass
                else:
                    try:
                        img_id = int(item)
                        img = Image.objects.filter(id=img_id).first()
                    except ValueError:
                        pass

                if img:
                    PostImage.objects.create(post=instance, image=img, order=index)
        else:
            # Fallback: add new images at the end
            current_count = instance.postimage_set.count()
            all_new = list(new_images_by_key.values()) + standard_new_images
            for index, img in enumerate(all_new):
                PostImage.objects.create(post=instance, image=img, order=current_count + index)

        # Handle videos update
        new_videos_by_key = {}
        for key in request.FILES:
            if key.startswith('video_new_'):
                vid = Video.objects.create(file=request.FILES[key], created_by=request.user)
                new_videos_by_key[key.replace('video_', '')] = vid

        standard_videos = request.FILES.getlist('videos')
        standard_new_videos = []
        if not new_videos_by_key and standard_videos:
            for video_file in standard_videos:
                vid = Video.objects.create(file=video_file, created_by=request.user)
                standard_new_videos.append(vid)

        video_order = request.data.getlist('video_order')
        if video_order:
            instance.postvideo_set.all().delete()
            for index, item in enumerate(video_order):
                vid = None
                if item.startswith('new_'):
                    if item in new_videos_by_key:
                        vid = new_videos_by_key[item]
                    else:
                        try:
                            new_idx = int(item.split('_')[1])
                            if new_idx < len(standard_new_videos):
                                vid = standard_new_videos[new_idx]
                        except (ValueError, IndexError):
                            pass
                else:
                    try:
                        vid_id = int(item)
                        vid = Video.objects.filter(id=vid_id).first()
                    except ValueError:
                        pass
                if vid:
                    PostVideo.objects.create(post=instance, video=vid, order=index)
        else:
            current_v_count = instance.postvideo_set.count()
            all_v_new = list(new_videos_by_key.values()) + standard_new_videos
            for index, vid in enumerate(all_v_new):
                PostVideo.objects.create(post=instance, video=vid, order=current_v_count + index)

        return instance


class RecommendedPostSerializer(serializers.ModelSerializer):
    created_by = UserSerializer(read_only=True)
    category = CategorySerializer(read_only=True)
    images = serializers.SerializerMethodField()
    videos = serializers.SerializerMethodField()
    likes_count = serializers.SerializerMethodField()
    comments_count = serializers.SerializerMethodField()
    is_liked = serializers.SerializerMethodField()
    is_saved = serializers.SerializerMethodField()
    latest_comment = serializers.SerializerMethodField()

    class Meta:
        model = Post
        fields = ['id', 'content', 'audience', 'created_at', 'updated_at', 'is_updated', 'is_active',
                  'created_by', 'category', 'images', 'videos', 'likes_count', 'comments_count', 'shared_count', 'is_liked', 'is_saved', 'latest_comment',
                  'allow_likes', 'allow_comments', 'allow_sharing', 'show_comments', 'show_likes_count', 'show_shares_count', 'allow_saving']
        read_only_fields = ['id', 'created_at', 'updated_at', 'is_updated', 'created_by']

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

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

    def get_is_liked(self, obj):
        from likes.models import LikePost
        request = self.context.get('request')
        if request and request.user.is_authenticated:
            return LikePost.objects.filter(user=request.user, post=obj).exists()
        return False

    def get_is_saved(self, obj):
        from saved.models import SavedPost
        request = self.context.get('request')
        if request and request.user.is_authenticated:
            return SavedPost.objects.filter(user=request.user, post=obj).exists()
        return False

    def get_latest_comment(self, obj):
        from comments.models import Comment
        from comments.serializers import CommentSerializer
        comment = Comment.objects.filter(post=obj, is_active=True).first()
        if comment:
            return CommentSerializer(comment, context=self.context).data
        return None

    def get_images(self, obj):
        return PostSerializer.get_images(self, obj)

    def get_videos(self, obj):
        return PostSerializer.get_videos(self, obj)