from django.contrib.auth import get_user_model
from rest_framework import serializers
from rest_framework.validators import UniqueValidator

from .models import AppUser, Article, Category, Task, Solution, TaskAttachment, TaskLabel, TaskStatusEnum


def get_task_status(user: AppUser, task: Task):
    solved = list(user.solutions.values_list('task', flat=True))
    if task.id in solved:
        return TaskStatusEnum.SOLVED

    required_tasks_ids = list(task.required_tasks.values_list('id', flat=True))
    if any(requirement not in solved for requirement in required_tasks_ids):
        return TaskStatusEnum.BLOCKED

    return TaskStatusEnum.OK


class TaskAttachmentSerializer(serializers.ModelSerializer):
    class Meta:
        model = TaskAttachment
        fields = "__all__"


class TaskLabelSerializer(serializers.ModelSerializer):
    def to_representation(self, tag):
        return tag.name

    class Meta:
        model = TaskLabel
        fields = "__all__"


class TaskListSolutionsSerializer(serializers.ModelSerializer):
    labels = TaskLabelSerializer(many=True, read_only=True)

    class Meta:
        model = Task
        fields = ['id', 'title', 'level', 'labels', 'points']


class TaskListSerializer(serializers.ModelSerializer):
    def get_status(self, task):
        user: AppUser = self.context['request'].user
        return get_task_status(user, task)

    labels = TaskLabelSerializer(many=True, read_only=True)
    status = serializers.SerializerMethodField()

    class Meta:
        model = Task
        fields = ['id', 'title', 'level', 'labels', 'status', 'points']


class SolutionsSerializer(serializers.ModelSerializer):
    task = TaskListSolutionsSerializer(read_only=True)

    class Meta:
        model = Solution
        fields = '__all__'


class RatingSolutionsSerializer(serializers.ModelSerializer):
    task = TaskListSolutionsSerializer(read_only=True)

    def to_representation(self, sol: Solution):
        representation = super().to_representation(sol)
        task_repr = representation.pop('task')
        representation['points'] = task_repr['points']
        return representation

    class Meta:
        model = Solution
        fields = ('date', 'task')


class UserRegisterSerializer(serializers.ModelSerializer):
    email = serializers.EmailField(
        required=True,
        validators=[UniqueValidator(queryset=AppUser.objects.all())]
    )
    password = serializers.CharField(write_only=True, required=True)

    class Meta:
        model = get_user_model()
        fields = ['id', 'username', 'email', 'password', 'solved']
        extra_kwargs = {
            'email': {'required': True},
        }

    def create(self, validated_data):
        return AppUser.objects.create_user(
            username=validated_data['username'],
            email=validated_data['email'],
            password=validated_data['password']
        )

class UserFullSerializer(serializers.ModelSerializer):
    points = serializers.ReadOnlyField()
    place = serializers.ReadOnlyField()

    class Meta:
        model = get_user_model()
        fields = "__all__"

class UserSerializerAbstract(serializers.ModelSerializer):
    place = serializers.SerializerMethodField()
    solutions = SolutionsSerializer(read_only=True, many=True)

    def get_place(self, user: AppUser):
        return len([el for el in AppUser.objects.all() if el.points > user.points]) + 1

    class Meta:
        abstract = True

class UserSerializer(UserSerializerAbstract):
    class Meta:
        model = get_user_model()
        fields = ['id', 'username', 'email', 'date_joined', 'points', 'solutions', 'place', 'solved']

class UserPublicSerializer(UserSerializerAbstract):
    class Meta:
        model = get_user_model()
        fields = ['id', 'username', 'date_joined', 'points', 'solutions', 'place', 'solved']

class UserRatingListSerializer(UserSerializerAbstract):
    class Meta:
        model = get_user_model()
        fields = ["id", "username", "points", "solved"]

class ArticlePublicSerializer(serializers.ModelSerializer):
    class Meta:
        model = Article
        exclude = ['linked_tasks']

class ArticleListSerializer(serializers.ModelSerializer):
    class Meta:
        model = Article
        fields = ['id', 'name', 'ordinal', 'excerpt']

class TaskSerializer(serializers.ModelSerializer):
    def get_status(self, task):
        user: AppUser = self.context['request'].user
        return get_task_status(user, task)

    status = serializers.SerializerMethodField()
    attachments = TaskAttachmentSerializer(many=True, read_only=True)
    labels = TaskLabelSerializer(many=True, read_only=True)
    articles = ArticleListSerializer(many=True, read_only=True)

    class Meta:
        model = Task
        exclude = ['flag']


class ArticleCategorySerializer(serializers.ModelSerializer):
    articles = ArticleListSerializer(many=True, read_only=True)

    class Meta:
        model = Category
        fields = '__all__'


class ArticleSerializer(serializers.ModelSerializer):
    linked_tasks = TaskSerializer(read_only=True, many=True)

    class Meta:
        model = Article
        fields = '__all__'


class TaskSolutionSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=True)
    flag = serializers.CharField(required=True)

    def create(self, validated_data):
        solution = self.context['solution']
        solution.save()
        return solution

    def validate(self, data):
        try:
            task = Task.objects.get(id=data.get('id'))
            if data.get('flag') == task.flag:
                user = self.context['request'].user
                solution, created = Solution.objects.get_or_create(user=user, task=task)
                if not created:
                    raise serializers.ValidationError({"flag": "Rozwiązanie zostało już wysłane"})

                self.context['solution'] = solution
                return data

            raise serializers.ValidationError({"flag": "Flaga niepoprawna"})

        except Task.DoesNotExist:
            raise serializers.ValidationError({"task": "Niepoprawny numer zadania"})


class ActivitySolutionsSerializer(serializers.ModelSerializer):
    task = TaskListSolutionsSerializer(read_only=True)
    user = UserRatingListSerializer(read_only=True)

    class Meta:
        model = Solution
        fields = '__all__'

