from django.db.models import Q, Prefetch
from rest_framework import serializers

from pulseapi.creators.models import EntryCreator
from pulseapi.entries.models import Entry
from pulseapi.profiles.models import UserBookmarks
from pulseapi.entries.serializers import (
    EntryWithCreatorsBaseSerializer,
    EntrySerializerWithV1Creators,
)
from .profiles import UserProfilePublicSerializer


class UserProfileEntriesSerializer(serializers.Serializer):
    """
    Serializes entries related to a profile based on the requested
    entry types.

    Add any combination of the following parameters to the serializer context
    to request specific types of entries in the serialized data:
    - `created` - List of entries created by the profile
    - `published` - List of entries published by the profile
    - `favorited` - List of entries favorited/bookmarked by the profile

    If none of these parameters are passed in, this serializer falls back to
    returning the number of entries (created, published, and favorited) associated
    with the profile.
    """
    @staticmethod
    def get_ordered_queryset(queryset, ordering_param=None, prefix=False):
        if not ordering_param:
            return queryset

        if prefix:
            if ordering_param[0] == '-':
                ordering_param = '-' + prefix + ordering_param[1:]
            else:
                ordering_param = prefix + ordering_param

        return queryset.order_by(ordering_param)

    def to_representation(self, instance):
        data = {}
        context = self.context
        user = context.get('user')
        include_created = context.get('created', False)
        include_published = context.get('published', False)
        include_favorited = context.get('favorited', False)
        created_ordering = context.get('created_ordering', False)
        published_ordering = context.get('published_ordering', False)
        favorited_ordering = context.get('favorited_ordering', False)
        EntrySerializerClass = context.get('EntrySerializerClass', EntryWithCreatorsBaseSerializer)

        # If none of the filter options are provided, only return the count of
        # entries associated with this profile
        if not (include_created or include_published or include_favorited):
            entry_count = Entry.objects.public().filter(
                Q(related_entry_creators__profile=instance) |
                Q(published_by=instance.user) |
                Q(bookmarked_by__profile=instance)
            ).distinct().count()
            return {
                'entry_count': entry_count
            }

        entry_queryset = Entry.objects.prefetch_related(
            'related_entry_creators__profile__related_user',
            'bookmarked_by__profile__related_user',
        )

        if include_created:
            entry_creators = UserProfileEntriesSerializer.get_ordered_queryset(
                queryset=(
                    EntryCreator.objects
                    .prefetch_related(Prefetch('entry', queryset=entry_queryset))
                    .filter(profile=instance)
                    .filter(entry__in=Entry.objects.public())
                ),
                ordering_param=created_ordering,
                prefix='entry__',
            )
            data['created'] = [
                EntrySerializerClass(
                    entry_creator.entry,
                    context={'user': user}
                ).data
                for entry_creator in entry_creators
            ]

        if include_published:
            if instance.user:
                entries = UserProfileEntriesSerializer.get_ordered_queryset(
                    queryset=entry_queryset.public().filter(published_by=instance.user),
                    ordering_param=published_ordering,
                )
            else:
                entries = []

            data['published'] = EntrySerializerClass(
                entries,
                context={'user': user},
                many=True
            ).data

        if include_favorited:
            user_bookmarks = UserProfileEntriesSerializer.get_ordered_queryset(
                queryset=(
                    UserBookmarks.objects.filter(profile=instance)
                    .filter(entry__in=Entry.objects.public())
                    .prefetch_related(Prefetch('entry', queryset=entry_queryset))
                ),
                ordering_param=favorited_ordering,
                prefix='entry__',
            )
            data['favorited'] = [
                EntrySerializerClass(bookmark.entry, context={'user': user}).data for bookmark in user_bookmarks
            ]

        return data


class UserProfilePublicWithEntriesSerializer(UserProfilePublicSerializer):
    """
    Serializes a user profile for public view and includes all entries
    associated with the profile
    """
    published_entries = serializers.SerializerMethodField()

    def get_published_entries(self, instance):
        user = instance.user

        return EntrySerializerWithV1Creators(
            user.entries.public().with_related().order_by('-id'),
            many=True,
            context={'user': self.context.get('user')}
        ).data if user else []

    created_entries = serializers.SerializerMethodField()

    def get_created_entries(self, instance):
        entry_creators = EntryCreator.objects.filter(profile=instance).order_by('-id')
        return [EntrySerializerWithV1Creators(x.entry).data for x in entry_creators if x.entry.is_approved()]
