from django.core.validators import MinValueValidator, MaxValueValidator
from django.db.models import Q
from rest_framework import serializers

from authenticate.serializers import PublicUserSerializer
from institute.serializers import (
    InstituteStudentSerializerForExamStat,
    InstituteSerializerForExamAndInstituteAndQuestionBank,
)
from quiz import conf
from quiz.models import (
    Quiz,
    QuizSheet,
    QuizTemplate,
    QUIZ_SHEET_STATE_SCORES_PUBLISHED,
    QUIZ_SHEET_STATE_NOT_ATTENDED,
    QuestionScore,
    QUIZ_TYPES,
)
from quiz.validators import (
    user_inputs_validator,
    token_quiz_id_quiz_sheet_id_entrance_token_validator,
)
from utils import validators as main_validators


def quiz_sort_by_validator(sort_by):
    from quiz.utils import list_quiz_sort_by_switcher

    allowed_sort_by_items = list_quiz_sort_by_switcher.keys()
    if sort_by not in allowed_sort_by_items:
        raise serializers.ValidationError(
            {"sort_by": f'sort_by should be one of {", ".join(allowed_sort_by_items)}'}
        )
    return sort_by


def quiz_type_validator(quiz_type):
    allowed_sort_by_items = [quiz_type_tuple[0] for quiz_type_tuple in QUIZ_TYPES]
    if quiz_type not in allowed_sort_by_items:
        raise serializers.ValidationError(
            {
                "quiz_type": f'quiz_type should be one of {", ".join(allowed_sort_by_items)}'
            }
        )
    return quiz_type


def quiz_sheet_sort_by_validator(sort_by):
    from quiz.utils import list_quiz_sheet_sort_by_switcher

    allowed_sort_by_items = list_quiz_sheet_sort_by_switcher.keys()
    if sort_by not in allowed_sort_by_items:
        raise serializers.ValidationError(
            {"sort_by": f'sort_by should be one of {", ".join(allowed_sort_by_items)}'}
        )
    return sort_by


class QuizListInputSerializer(serializers.Serializer):
    context_institute_id = serializers.UUIDField()
    q = serializers.CharField(max_length=100, required=False, allow_null=True)
    qid = serializers.CharField(max_length=100, required=False, allow_null=True)
    skip = serializers.IntegerField(validators=[MinValueValidator(0)])
    take = serializers.IntegerField(
        validators=[MinValueValidator(1), MaxValueValidator(100)]
    )
    sort_by = serializers.CharField(validators=[quiz_sort_by_validator])
    reanswerable_filter = serializers.CharField(
        validators=[main_validators.IncludeExcludeOnlyValidator()]
    )
    shuffle_questions_filter = serializers.CharField(
        validators=[main_validators.IncludeExcludeOnlyValidator()]
    )
    shuffle_choices_filter = serializers.CharField(
        validators=[main_validators.IncludeExcludeOnlyValidator()]
    )
    single_question_at_a_time_filter = serializers.CharField(
        validators=[main_validators.IncludeExcludeOnlyValidator()]
    )
    archived_filter = serializers.CharField(
        validators=[main_validators.IncludeExcludeOnlyValidator()]
    )

    expired_filter = serializers.CharField(
        validators=[main_validators.IncludeExcludeOnlyValidator()]
    )


class QuizTemplateListInputSerializer(serializers.Serializer):
    context_institute_id = serializers.UUIDField()
    q = serializers.CharField(max_length=100, required=False, allow_null=True)
    skip = serializers.IntegerField(validators=[MinValueValidator(0)])
    take = serializers.IntegerField(
        validators=[MinValueValidator(1), MaxValueValidator(100)]
    )
    sort_by = serializers.CharField(validators=[quiz_sort_by_validator])
    reanswerable_filter = serializers.CharField(
        validators=[main_validators.IncludeExcludeOnlyValidator()]
    )
    shuffle_questions_filter = serializers.CharField(
        validators=[main_validators.IncludeExcludeOnlyValidator()]
    )
    shuffle_choices_filter = serializers.CharField(
        validators=[main_validators.IncludeExcludeOnlyValidator()]
    )
    single_question_at_a_time_filter = serializers.CharField(
        validators=[main_validators.IncludeExcludeOnlyValidator()]
    )
    archived_filter = serializers.CharField(
        validators=[main_validators.IncludeExcludeOnlyValidator()]
    )


class QuizTemplateMarketInputSerializer(serializers.Serializer):
    q = serializers.CharField(max_length=100, required=False, allow_null=True)
    skip = serializers.IntegerField(validators=[MinValueValidator(0)])
    take = serializers.IntegerField(
        validators=[MinValueValidator(1), MaxValueValidator(100)]
    )
    sort_by = serializers.CharField(validators=[quiz_sort_by_validator])
    reanswerable_filter = serializers.CharField(
        validators=[main_validators.IncludeExcludeOnlyValidator()]
    )
    shuffle_questions_filter = serializers.CharField(
        validators=[main_validators.IncludeExcludeOnlyValidator()]
    )
    shuffle_choices_filter = serializers.CharField(
        validators=[main_validators.IncludeExcludeOnlyValidator()]
    )
    single_question_at_a_time_filter = serializers.CharField(
        validators=[main_validators.IncludeExcludeOnlyValidator()]
    )
    archived_filter = serializers.CharField(
        validators=[main_validators.IncludeExcludeOnlyValidator()]
    )


class QuizSheetForInstituteModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = QuizSheet
        fields = [
            "referer_id",
            "allowed_start_at",
            "allowed_finish_at",
            "duration",
            "final_score",
            "state",
            "entrance_token",
        ]


class QuizSheetModelSerializerForHistory(serializers.ModelSerializer):
    class Meta:
        model = QuizSheet
        fields = [
            "referer_id",
            "entrance_date",
            "allowed_start_at",
            "allowed_finish_at",
            "duration",
            "final_score",
            "state",
        ]


class QuizModelBasicSerializer(serializers.ModelSerializer):
    class Meta:
        model = Quiz
        fields = ["id", "name", "poster", "poster_thumbnail", "quiz_type"]


class QuizModelForReportSheetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Quiz
        fields = [
            "id",
            "name",
            "poster",
            "poster_thumbnail",
            "quiz_type",
            "custom_report_card",
        ]


class QuizSheetModelForEnterSerializer(serializers.ModelSerializer):
    solutions_sheet = serializers.SerializerMethodField(
        method_name="get_solutions_sheet"
    )
    questions_sheet = serializers.SerializerMethodField(
        method_name="get_questions_sheet"
    )
    quizee_inputs_sheet = serializers.SerializerMethodField(
        method_name="get_quizee_inputs_sheet"
    )
    quiz = QuizModelBasicSerializer()

    def get_solutions_sheet(self, instance):
        if instance.state == QUIZ_SHEET_STATE_SCORES_PUBLISHED:
            return instance._solutions_sheet
        else:
            return None

    def get_questions_sheet(self, instance):
        if instance.state != QUIZ_SHEET_STATE_NOT_ATTENDED:
            return instance._questions_sheet
        else:
            return None

    def get_quizee_inputs_sheet(self, instance):
        if instance.state != QUIZ_SHEET_STATE_NOT_ATTENDED:
            return instance._quizee_inputs_sheet
        else:
            return None

    class Meta:
        model = QuizSheet
        fields = [
            "referer_id",
            "solutions_sheet",
            "questions_sheet",
            "quizee_inputs_sheet",
            "entrance_date",
            "allowed_start_at",
            "allowed_finish_at",
            "duration",
            "final_score",
            "state",
            "report_card",
            "quiz",
        ]


class QuizSheetModelForReportCardSerializer(serializers.ModelSerializer):
    user = PublicUserSerializer()
    institute_student = InstituteStudentSerializerForExamStat()
    solutions_sheet = serializers.SerializerMethodField(
        method_name="get_solutions_sheet"
    )
    questions_sheet = serializers.SerializerMethodField(
        method_name="get_questions_sheet"
    )
    quizee_inputs_sheet = serializers.SerializerMethodField(
        method_name="get_quizee_inputs_sheet"
    )
    corrections = serializers.SerializerMethodField("corrections_generator")
    quiz = QuizModelForReportSheetSerializer()

    def get_solutions_sheet(self, instance):
        return instance._solutions_sheet

    def get_questions_sheet(self, instance):
        return instance._questions_sheet

    def get_quizee_inputs_sheet(self, instance):
        return instance._quizee_inputs_sheet

    def corrections_generator(self, quiz_sheet):
        corrections = QuestionScore.objects.filter(quiz_sheet=quiz_sheet)
        return QuestionScoreModelForStatSerializer(corrections, many=True).data

    class Meta:
        model = QuizSheet
        fields = [
            "referer_id",
            "final_score",
            "state",
            "entrance_date",
            "finished_at",
            "report_card",
            "solutions_sheet",
            "questions_sheet",
            "quizee_inputs_sheet",
            "user",
            "institute_student",
            "corrections",
            "quiz",
        ]


class QuizSheetModelForStatSerializer(QuizSheetModelForReportCardSerializer):
    pass


class GetQuizSheetForListSerializer(serializers.ModelSerializer):
    user = PublicUserSerializer()
    institute_student = InstituteStudentSerializerForExamStat()
    quiz = QuizModelBasicSerializer()

    class Meta:
        model = QuizSheet
        fields = [
            "referer_id",
            "state",
            "entrance_date",
            "final_score",
            "duration",
            "user",
            "institute_student",
            "quiz",
        ]


class QuestionScoreModelForStatSerializer(serializers.ModelSerializer):
    class Meta:
        model = QuestionScore
        fields = ["question_id", "score", "blank"]


class QuizTemplateModelSerializer(serializers.ModelSerializer):
    blueprint = serializers.SerializerMethodField(method_name="get_blueprint")

    def get_blueprint(self, instance):
        if self.context.get("with_blueprint") == True:
            return instance._blueprint
        else:
            return None

    class Meta:
        model = QuizTemplate
        fields = [
            "id",
            "name",
            "quiz_type",
            "single_question_at_a_time",
            "reanswerable",
            "shuffle_questions",
            "shuffle_choices",
            "apply_negative_scores",
            "poster",
            "poster_thumbnail",
            "theme",
            "description",
            "duration",
            "questions_count",
            "finish_message",
            "archived",
            "created_at",
            "modified_at",
            "price",
            "wage_free",
            "vax_free",
            "public",
            "featured",
            "start",
            "end",
            "blueprint",
        ]


class QuizModelSerializer(QuizTemplateModelSerializer):
    quiz_sheet_count = serializers.SerializerMethodField(
        method_name="get_quiz_sheet_count"
    )
    not_corrected_quiz_sheet_count = serializers.SerializerMethodField(
        method_name="get_not_corrected_quiz_sheet_count"
    )

    def get_quiz_sheet_count(self, instance):
        return QuizSheet.objects.filter(quiz=instance).count()

    def get_not_corrected_quiz_sheet_count(self, instance):
        return (
            QuizSheet.objects.filter(quiz=instance)
            .filter(~Q(state=QUIZ_SHEET_STATE_SCORES_PUBLISHED))
            .count()
        )

    class Meta:
        model = Quiz
        fields = QuizTemplateModelSerializer.Meta.fields + [
            "creator",
            "last_editor",
            "quiz_sheet_count",
            "not_corrected_quiz_sheet_count",
        ]


class QuizGetModelSerializer(QuizModelSerializer):
    institute = InstituteSerializerForExamAndInstituteAndQuestionBank()

    class Meta:
        model = Quiz
        fields = QuizModelSerializer.Meta.fields + ["institute"]


class QuizTemplateModelSerializer(serializers.ModelSerializer):
    blueprint = serializers.SerializerMethodField(method_name="get_blueprint")

    def get_blueprint(self, instance):
        if self.context.get("with_blueprint") == True:
            return instance._blueprint
        else:
            return None

    class Meta:
        model = QuizTemplate
        fields = [
            "id",
            "name",
            "single_question_at_a_time",
            "reanswerable",
            "shuffle_questions",
            "shuffle_choices",
            "apply_negative_scores",
            "poster",
            "poster_thumbnail",
            "theme",
            "description",
            "duration",
            "questions_count",
            "finish_message",
            "archived",
            "created_at",
            "modified_at",
            "price",
            "public",
            "featured",
            "blueprint",
        ]


class GetQuizInputSerializer(serializers.Serializer):
    id = serializers.CharField()


class GetQuizTemplateInputSerializer(serializers.Serializer):
    id = serializers.CharField()


class GetQuizSheetListInputSerializer(serializers.Serializer):
    quiz_id = serializers.CharField()
    skip = serializers.IntegerField(validators=[MinValueValidator(0)])
    take = serializers.IntegerField(
        validators=[MinValueValidator(1), MaxValueValidator(100)]
    )
    sort_by = serializers.CharField(validators=[quiz_sheet_sort_by_validator])
    name_filter = serializers.CharField(required=False)


class GetQuizSheetAttendedListInputSerializer(serializers.Serializer):
    skip = serializers.IntegerField(validators=[MinValueValidator(0)])
    take = serializers.IntegerField(
        validators=[MinValueValidator(1), MaxValueValidator(100)]
    )
    sort_by = serializers.CharField(validators=[quiz_sheet_sort_by_validator])
    name_filter = serializers.CharField(required=False)


class GetQuizStatInputSerializer(serializers.Serializer):
    quiz_id = serializers.CharField()
    skip = serializers.IntegerField(validators=[MinValueValidator(0)])
    take = serializers.IntegerField(
        validators=[MinValueValidator(1), MaxValueValidator(100)]
    )
    sort_by = serializers.CharField(validators=[quiz_sheet_sort_by_validator])
    name_filter = serializers.CharField(required=False)


def quiz_poster_valdator(value):
    main_validators.image_validetor(
        image=value,
        image_size_as_MB=conf.quiz_poster_maximum_size_mb,
        format=conf.quiz_poster_allowed_formats,
        max_side=conf.quiz_poster_maximum_side_size,
    )
    return value


class CreateQuizInputSerializer(serializers.Serializer):
    context_institute_id = serializers.CharField()
    name = serializers.CharField(max_length=500, min_length=3)
    rule = serializers.JSONField()
    single_question_at_a_time = serializers.BooleanField()
    public = serializers.BooleanField()
    reanswerable = serializers.BooleanField()

    quiz_type = serializers.CharField(validators=[quiz_type_validator])

    shuffle_questions = serializers.BooleanField()
    shuffle_choices = serializers.BooleanField()
    apply_negative_scores = serializers.BooleanField()

    poster = serializers.ImageField(required=False, validators=[quiz_poster_valdator])
    start = serializers.DateTimeField(required=False)
    end = serializers.DateTimeField(required=False)
    duration = serializers.DurationField(required=False)
    description = serializers.CharField(
        max_length=3000, required=False, allow_blank=True
    )
    theme = serializers.CharField(max_length=20)

    price = serializers.IntegerField(required=False, min_value=0, default=0)
    finish_message = serializers.CharField(required=False, max_length=1000)


class CreateQuizTemplateInputSerializer(serializers.Serializer):
    quiz_id = serializers.CharField()


class EditQuizInputSerializer(CreateQuizInputSerializer):
    quiz_id = serializers.CharField()
    remove_poster = serializers.BooleanField(default=False, required=False)
    rule = serializers.JSONField(required=False)


class QuizReportCardTestInputSerializer(serializers.Serializer):
    quiz_id = serializers.CharField()


class EditQuizTemplateInputSerializer(CreateQuizInputSerializer):
    quiz_template_id = serializers.CharField()
    remove_poster = serializers.BooleanField(default=False, required=False)
    rule = serializers.JSONField(required=False)


class ArchiveQuizInputSerializer(serializers.Serializer):
    context_institute_id = serializers.CharField()
    quiz_id = serializers.CharField()


class ArchiveQuizTemplateInputSerializer(serializers.Serializer):
    context_institute_id = serializers.CharField()
    quiz_template_id = serializers.CharField()


class BaseTokenQuizIDQuizSheetIDEntranceTokenSerializer(serializers.Serializer):
    token = serializers.CharField(required=False)
    quiz_id = serializers.CharField(required=False)
    quiz_sheet_id = serializers.CharField(required=False)
    entrance_token = serializers.CharField(required=False)

    def validate(self, data):
        token_quiz_id_quiz_sheet_id_entrance_token_validator(data)

        return data


class QuizGateWayInputSerializer(BaseTokenQuizIDQuizSheetIDEntranceTokenSerializer):
    pass


class QuizEnterInputSerializer(BaseTokenQuizIDQuizSheetIDEntranceTokenSerializer):
    pass


class SetAnswerInputSerializer(BaseTokenQuizIDQuizSheetIDEntranceTokenSerializer):
    user_inputs = serializers.JSONField()

    def validate(self, data):
        super().validate(data)
        if "user_inputs" not in data:
            raise serializers.ValidationError({"user_inputs": "This field is required"})
        user_inputs_validator(data["user_inputs"])
        return data


class FinishQuizSheetInputSerializer(BaseTokenQuizIDQuizSheetIDEntranceTokenSerializer):
    pass


class QuizSheetReportInputSerializer(BaseTokenQuizIDQuizSheetIDEntranceTokenSerializer):
    pass


class QuizSheetAttendedGetInputSerializer(serializers.Serializer):
    quiz_sheet_id = serializers.CharField(required=True)


class QuizSheetGetInputSerializer(serializers.Serializer):
    quiz_sheet_id = serializers.CharField(required=True)


class InvoiceInputSerializer(serializers.Serializer):
    token = serializers.CharField(required=False)
    quiz_id = serializers.CharField(required=False)

    # not supported yet
    # quiz_sheet_id = serializers.CharField(required=False)
    # entrance_token = serializers.CharField(required=False)

    def validate(self, data):
        token_quiz_id_quiz_sheet_id_entrance_token_validator(data)

        return data


class GetQuizIdFromExamIdInputValidator(serializers.Serializer):
    wikiazma_exam_id = serializers.CharField(required=True)


class GenerateQuizSheetInputValidator(serializers.Serializer):
    wikiazma_institute_student_id = serializers.CharField(required=False)
    referer_institute_student_identity = serializers.CharField(required=False)
    wikiazma_quiz_id = serializers.CharField()
    allowed_start_at = serializers.DateTimeField(required=False)
    allowed_finish_at = serializers.DateTimeField(required=False)
    duration = serializers.DurationField(required=False)

    def validate(self, data):
        """
        One out of many fields should be present.
        """
        fields = ["wikiazma_institute_student_id", "referer_institute_student_identity"]
        one_field_found = False
        for field in fields:
            if field in data:
                if one_field_found:
                    raise serializers.ValidationError(
                        "One of " + " and ".join(fields) + " is required."
                    )
                one_field_found = True
        return data


class VerifyQuizSheetInputValidator(serializers.Serializer):
    quiz_sheet_id = serializers.CharField()


class QuizSheetCorrectionAddInputValidator(serializers.Serializer):
    quiz_sheet_id = serializers.CharField()
    index = serializers.IntegerField()
    score = serializers.FloatField()
    comment_for_student = serializers.CharField(required=False)
    comment_for_other_correctors = serializers.CharField(required=False)


minimum_quiz_sheet_fields = [
    "referer_id",
    "state",
    "entrance_date",
    "allowed_start_at",
    "allowed_finish_at",
    "duration",
]


class GetQuizSheetMinimumDataForStudentsSerializer(serializers.ModelSerializer):
    class Meta:
        model = QuizSheet
        fields = minimum_quiz_sheet_fields


class GetQuizSheetForInstituteSerializer(serializers.ModelSerializer):
    class Meta:
        model = QuizSheet
        fields = ["referer_id", "state", "entrance_date", "final_score", "report_card"]
