import logging

from django.core.exceptions import ObjectDoesNotExist
from drf_dynamic_fields import DynamicFieldsMixin
from rest_framework import serializers

from core import models
from core.models import (ExamType, Feedback, GeneralTaskSubscription,
                         StudentInfo, Submission, SubmissionType,
                         TutorSubmissionAssignment)
from util.factories import GradyUserFactory

log = logging.getLogger(__name__)
user_factory = GradyUserFactory()


class DynamicFieldsModelSerializer(DynamicFieldsMixin,
                                   serializers.ModelSerializer):
    pass


class ExamSerializer(DynamicFieldsModelSerializer):

    class Meta:
        model = ExamType
        fields = ('module_reference', 'total_score',
                  'pass_score', 'pass_only',)


class FeedbackSerializer(DynamicFieldsModelSerializer):
    assignment_id = serializers.UUIDField(write_only=True)

    def validate(self, data):
        log.debug(data)
        assignment_id = data.pop('assignment_id')
        score = data.get('score')

        try:
            assignment = TutorSubmissionAssignment.objects.get(
                assignment_id=assignment_id)
        except ObjectDoesNotExist as err:
            raise serializers.ValidationError('No assignment for given id.')

        submission = assignment.submission
        if not 0 <= score <= submission.type.full_score:
            raise serializers.ValidationError(
                f'Score has to be in range [0..{submission.type.full_score}].')

        if hasattr(submission, 'feedback'):
            raise serializers.ValidationError(
                'Feedback for this submission already exists')

        return {
            **data,
            'assignment': assignment,
            'of_submission': submission
        }

    def create(self, validated_data) -> Feedback:
        assignment = validated_data.pop('assignment')
        assignment.set_done()

        return Feedback.objects.create(**validated_data)

    class Meta:
        model = Feedback
        fields = ('assignment_id', 'text', 'score')


class SubmissionTypeSerializer(DynamicFieldsModelSerializer):

    class Meta:
        model = SubmissionType
        fields = ('name', 'full_score', 'description', 'solution')


class SubmissionSerializer(DynamicFieldsModelSerializer):
    feedback = serializers.ReadOnlyField(source='feedback.text')
    score = serializers.ReadOnlyField(source='feedback.score')
    type_id = serializers.ReadOnlyField(source='type.id')
    type_name = serializers.ReadOnlyField(source='type.name')
    full_score = serializers.ReadOnlyField(source='type.full_score')

    class Meta:
        model = Submission
        fields = ('type_id', 'type_name', 'text',
                  'feedback', 'score', 'full_score')


class StudentInfoSerializer(DynamicFieldsModelSerializer):
    name = serializers.ReadOnlyField(source='user.fullname')
    user = serializers.ReadOnlyField(source='user.username')
    exam = ExamSerializer()
    submissions = SubmissionSerializer(many=True)

    class Meta:
        model = StudentInfo
        fields = ('name', 'user', 'exam', 'submissions')


class SubmissionNoTextFieldsSerializer(DynamicFieldsModelSerializer):
    score = serializers.ReadOnlyField(source='feedback.score')
    type = serializers.ReadOnlyField(source='type.name')
    full_score = serializers.ReadOnlyField(source='type.full_score')

    class Meta:
        model = Submission
        fields = ('type', 'score', 'full_score')


class StudentInfoSerializerForListView(DynamicFieldsModelSerializer):
    name = serializers.ReadOnlyField(source='user.fullname')
    user = serializers.ReadOnlyField(source='user.username')
    exam = serializers.ReadOnlyField(source='exam.module_reference')
    submissions = SubmissionNoTextFieldsSerializer(many=True)

    class Meta:
        model = StudentInfo
        fields = ('name', 'user', 'exam', 'submissions')


class TutorSerializer(DynamicFieldsModelSerializer):
    feedback_count = serializers.IntegerField(source='get_feedback_count',
                                              read_only=True)

    def create(self, validated_data) -> models.UserAccount:
        log.info("Crating tutor from data %s", validated_data)
        return user_factory.make_tutor(
            username=validated_data['username'])

    class Meta:
        model = models.UserAccount
        fields = ('username', 'feedback_count')


class AssignmentSerializer(DynamicFieldsModelSerializer):
    submission_id = serializers.ReadOnlyField(
        source='submission.submission_id')

    class Meta:
        model = TutorSubmissionAssignment
        fields = ('assignment_id', 'submission_id', 'is_done',)


class SubmissionAssignmentSerializer(DynamicFieldsModelSerializer):
    text = serializers.ReadOnlyField()
    type_id = serializers.ReadOnlyField(source='type.id')
    full_score = serializers.ReadOnlyField(source='type.full_score')

    class Meta:
        model = Submission
        fields = ('submission_id', 'type_id', 'text', 'full_score')


class AssignmentDetailSerializer(DynamicFieldsModelSerializer):
    submission = SubmissionAssignmentSerializer()
    feedback = FeedbackSerializer(source='submission.feedback')

    class Meta:
        model = TutorSubmissionAssignment
        fields = ('assignment_id', 'feedback', 'submission', 'is_done',)


class SubscriptionSerializer(DynamicFieldsModelSerializer):
    owner = serializers.ReadOnlyField(source='owner.username')
    query_key = serializers.CharField(required=False)
    assignments = AssignmentSerializer(read_only=True, many=True)

    def validate(self, data):
        data['owner'] = self.context['request'].user

        if 'query_key' in data != \
                data['query_type'] == GeneralTaskSubscription.RANDOM:
            raise serializers.ValidationError(
                f'The {data["query_type"]} query_type does not work with the'
                f'provided key')

        try:
            GeneralTaskSubscription.objects.get(
                owner=data['owner'],
                query_type=data['query_type'],
                query_key=data.get('query_key', None))
        except ObjectDoesNotExist:
            pass
        else:
            raise serializers.ValidationError(
                'The user already has the subscription')

        return data

    def create(self, validated_data) -> GeneralTaskSubscription:
        return GeneralTaskSubscription.objects.create(**validated_data)

    class Meta:
        model = GeneralTaskSubscription
        fields = (
            'subscription_id',
            'owner',
            'query_type',
            'query_key',
            'assignments')