from django.contrib.auth.models import User
from drf_writable_nested import WritableNestedModelSerializer
from rest_framework import serializers
from rest_framework.generics import get_object_or_404
from rest_framework.relations import PrimaryKeyRelatedField

from pigeon.auth.serializers import UserSerializer
from pigeon.blog.images.serializers import ChannelImageSerializer
from pigeon.blog.tags.serializers import ChannelTagSerializer
from pigeon.blog.utils.utils import BlogSerializerUtils
from pigeon.models import Channel, Tag, Post, ChannelImage


class ChannelSerializer(WritableNestedModelSerializer):
    channel_access = PrimaryKeyRelatedField(many=True, write_only=True, queryset=User.objects.all(), required=False)
    has_access = serializers.SerializerMethodField()
    tags = serializers.SerializerMethodField()
    owner = UserSerializer(many=False, read_only=True, allow_null=False)
    number_of_members = serializers.SerializerMethodField()
    number_of_posts = serializers.SerializerMethodField()
    channel_image = ChannelImageSerializer(many=True, read_only=True)

    class Meta:
        model = Channel
        fields = "__all__"
        read_only_fields = ("id",)
        extra_kwargs = {'password': {'write_only': True}}

    def to_internal_value(self, data: dict):
        """
        Method for translating author id from request to nested serializer user object
        """
        user_id = self.get_user_id_from_request()
        modified_data = BlogSerializerUtils.add_values_to_dict(data, owner=user_id)
        self.fields['owner'] = serializers.PrimaryKeyRelatedField(queryset=User.objects.all())
        return super(ChannelSerializer, self).to_internal_value(modified_data)

    def to_representation(self, instance: Channel):
        self.fields['owner'] = UserSerializer(many=False, read_only=True, allow_null=False)
        return super(ChannelSerializer, self).to_representation(instance)

    def get_user_from_request(self):
        return self.context['request'].user

    def get_has_access(self, channel: Channel) -> bool:
        """
        Get bool if user has access to channel
        """
        user = self.get_user_from_request()
        return user in channel.channel_access.all() or user == channel.owner

    def get_number_of_members(self, channel: Channel) -> int:
        """
        Get number of members in channel
        """
        return channel.channel_access.count()

    def get_number_of_posts(self, channel: Channel) -> int:
        """
        Get number of posts in channel
        """
        return Post.objects.filter(channel=channel.id).count()

    def get_channel_by_id(self, id: int) -> Channel:
        """
        Get channel by id
        """
        return get_object_or_404(Channel, id=id)

    def get_tags(self, channel: Channel):
        """
        Get tags assigned to Post
        """
        tags = Tag.objects.filter(channel=channel.id)
        serializer = ChannelTagSerializer(tags, many=True)
        return serializer.data

    def get_user_id_from_request(self):
        return self.context['request'].user.id

    def create(self, validated_data):
        """
        Create channel with autogenerated password
        """
        user_id = self.get_user_id_from_request()
        channel = Channel(**validated_data)
        channel.save()
        channel.channel_access.add(user_id)
        channel.password = User.objects.make_random_password()
        if 'tags' in self.context['request'].data:
            tag_list_data = self.context['request'].data['tags']
            tags = [Tag(**tag_data) for tag_data in tag_list_data]
            self.link_tags(channel, tags)
        return channel

    def link_tags(self, channel: Channel, tags_to_be_added: list):
        existing_tags = Tag.objects.filter(name__in=[tag.name for tag in tags_to_be_added])
        for tag in tags_to_be_added:
            existing_tag = existing_tags.filter(name=tag.name).first()
            if not existing_tag:
                existing_tag = tag
                existing_tag.save()
            existing_tag.channel.add(channel.id)

    def save_image(self, image, channel: Channel):
        photo = ChannelImage.objects.create(image=image, channel=channel)
        photo.save()

    def update(self, instance, validated_data):
        user = self.get_user_id_from_request()
        if instance.owner is not None:
            if user == instance.owner.id:
                tag_list_data = self.context['request'].data['tags']
                tags = [Tag(**tag_data) for tag_data in tag_list_data]
                channel_tags = Tag.objects.filter(channel=instance.id)
                for tag in channel_tags:
                    if tag not in tags:
                        tag = Tag.objects.get(id=tag.id)
                        tag.channel.remove(instance)
                self.link_tags(instance, tags)
                return super(ChannelSerializer, self).update(instance, validated_data)
            raise serializers.ValidationError(detail=f'User {user} not owner of channel with id {instance.id}')
        raise serializers.ValidationError(detail=f"Channel with id {instance.id} has no owner, contact with admin")
