"""This module holds the v2 api-related serializers for the ``workshops`` app"""

from rest_framework import serializers

from mtp.apps.trainings.apiv2.serializers import TrainingActivitySerializer

from ..models import (
    Workshop,
    WorkshopStep,
    WorkshopStepImage,
    WorkshopStepQuestion,
    WorkshopStepQuestionAnswer,
    WorkshopStepQuestionUserAnswer
)


class WorkshopStepQuestionAnswerSerializer(serializers.ModelSerializer):
    """Serializer for the ``WorkshopStepQuestionAnswer`` model"""

    class Meta:
        model = WorkshopStepQuestionAnswer
        fields = (
            'id',
            'label',
            'color',
            'is_correct',
            'points'
        )


class WorkshopStepSerializer(serializers.ModelSerializer):
    """Parent serializer for the ``WorkshopStep`` models"""

    class Meta:
        model = WorkshopStep
        fields = (
            'id',
            'sort_order',
            'step_name',
            'title',
            'description'
        )


class WorkshopStepQuestionSerializer(WorkshopStepSerializer):
    """Serializer for the ``WorkshopStepQuestion`` model"""

    answers = serializers.SerializerMethodField()

    class Meta:
        model = WorkshopStepQuestion
        fields = WorkshopStepSerializer.Meta.fields + (
            'is_multiple_choices',
            'is_all_or_nothing',
            'answers'
        )

    def get_answers(self, instance):
        """Get the answers for the current ``Workshop``

        For the parameters, see ``rest_framework.serializers.ModelSerializer``

        """

        return WorkshopStepQuestionAnswerSerializer(
            instance.answers.non_deleted(),
            many=True,
            read_only=True
        ).data


class WorkshopStepImageSerializer(WorkshopStepSerializer):
    """Serializer for the ``WorkshopStepImage`` model"""

    class Meta:
        model = WorkshopStepImage
        fields = WorkshopStepSerializer.Meta.fields + (
            'image',
        )


class WorkshopSerializer(TrainingActivitySerializer, serializers.ModelSerializer):
    """Serializer for the ``Workshop`` model"""

    steps = serializers.SerializerMethodField()

    class Meta:
        model = Workshop
        fields = TrainingActivitySerializer.Meta.fields + (
            'steps',
            'enable_score'
        )

    def get_steps(self, instance):
        """Get the steps for the current ``Workshop``

        For the parameters, see ``rest_framework.serializers.ModelSerializer``

        """

        return [
            step.serializer_class(step).data
            for step in instance.steps.non_deleted()
        ]


class WorkshopAnswerTraineeScoreSerializer(serializers.ModelSerializer):
    """Serializer for the results of a given trainee for a given workshop answer"""

    is_selected = serializers.SerializerMethodField()
    session_rate = serializers.SerializerMethodField()

    class Meta:
        model = WorkshopStepQuestionAnswer
        fields = (
            'id',
            'label',
            'is_correct',
            'is_selected',
            'session_rate',
        )

    def get_is_selected(self, instance):
        """Get the selection status of this answer for the current trainee

        For the parameters, see ``rest_framework.serializers.ModelSerializer``

        Returns
        -------
        bool
            True if the answer was selected by the trainee,
            False otherwise

        """

        return instance.is_selected(
            self.context['trainee'],
            self.context['training_session']
        )

    def get_session_rate(self, instance):
        """Get the answer rate for the provided session

        For the parameters, see ``rest_framework.serializers.ModelSerializer``

        Returns
        -------
        float
            The percentage of the responding user who picked this answer

        """

        return instance.session_rate(
            self.context['training_session']
        )


class WorkshopQuestionTraineeScoreSerializer(serializers.ModelSerializer):
    """Serializer for the results of a given trainee for a given workshop question"""

    score = serializers.SerializerMethodField()
    average_score = serializers.SerializerMethodField()
    answers = serializers.SerializerMethodField()
    open_answer = serializers.SerializerMethodField()

    class Meta:
        model = WorkshopStepQuestion
        fields = (
            'id',
            'title',
            'score',
            'average_score',
            'max_points_yield',
            'answers',
            'open_answer',
            'is_open',
            'is_single_choice',
            'is_all_or_nothing',
        )

    def get_score(self, instance):
        """Get the trainee result for this workshop question

        For the parameters, see ``rest_framework.serializers.ModelSerializer``"""

        return instance.get_trainee_session_score(
            self.context['trainee'],
            self.context['training_session']
        )

    def get_average_score(self, instance):
        """Get the training session average trainee result for this workshop

        For the parameters, see ``rest_framework.serializers.ModelSerializer``"""

        return instance.get_session_average_score(
            self.context['training_session']
        )

    def get_answers(self, instance):
        """Get the answers for this question

        For the parameters, see ``rest_framework.serializers.ModelSerializer``

        Returns
        -------
        OrderedDict
            A list of answer for the current question

        """

        return WorkshopAnswerTraineeScoreSerializer(
            instance.answers.non_deleted(),
            many=True,
            context=self.context
        ).data

    def get_open_answer(self, instance):
        """Get the "open" answer for this question

        For the parameters, see ``rest_framework.serializers.ModelSerializer``

        Returns
        -------
        str or None
            str if this answer is an ``open`` one and if the user did write an answer,
            None otherwise

        """

        # The question is not an "open" one, abort
        if not instance.is_open:
            return None

        try:
            return WorkshopStepQuestionUserAnswer.objects.get_open_answer_for_question(
                question=instance,
                training_session=self.context['training_session'],
                trainee=self.context['trainee']
            ).text

        except (WorkshopStepQuestionUserAnswer.MultipleObjectsReturned,
                WorkshopStepQuestionUserAnswer.DoesNotExist):
            return None


class WorkshopTraineeDetailedScoreSerializer(serializers.ModelSerializer):
    """Serializer for the detailed results of a given trainee for a given workshop"""

    score = serializers.SerializerMethodField()
    average_score = serializers.SerializerMethodField()
    questions = serializers.SerializerMethodField()

    class Meta:
        model = Workshop
        fields = (
            'id',
            'name',
            'score',
            'average_score',
            'questions',
        )

    def get_score(self, instance):
        """Get the trainee result for this workshop

        For the parameters, see ``rest_framework.serializers.ModelSerializer``

        Returns
        -------
        int
            The trainee's score for this workshop

        """

        return instance.get_trainee_session_score(
            self.context['training_session'],
            self.context['trainee']
        )

    def get_average_score(self, instance):
        """Get the training session average trainee result for this workshop

        For the parameters, see ``rest_framework.serializers.ModelSerializer``

        Returns
        -------
        float
            The workshop average session's score

        """

        return instance.get_training_session_average_score(
            self.context['training_session']
        )

    def get_questions(self, instance):
        """Get the questions along with their score for this workshop

        For the parameters, see ``rest_framework.serializers.ModelSerializer``

        Returns
        -------
        OrderedDict
            A list of questions for the current workshop

        """

        return WorkshopQuestionTraineeScoreSerializer(
            instance.steps.instance_of(WorkshopStepQuestion).non_deleted(),
            many=True,
            context=self.context
        ).data


class WorkshopTraineeScoreSerializer(serializers.ModelSerializer):
    """Serializer for the results of a given trainee for a given workshop"""

    score = serializers.SerializerMethodField()
    average_score = serializers.SerializerMethodField()
    maximum_score = serializers.SerializerMethodField()

    class Meta:
        model = Workshop
        fields = (
            'score',
            'average_score',
            'maximum_score',
        )

    def get_score(self, instance):
        """Get the trainee result for this workshop

        For the parameters, see ``rest_framework.serializers.ModelSerializer``

        Returns
        -------
        int
            The trainee's score for this workshop

        """

        return instance.get_trainee_session_score(
            self.context['training_session'],
            self.context['trainee']
        )

    def get_average_score(self, instance):
        """Get the training session average trainee result for this workshop

        For the parameters, see ``rest_framework.serializers.ModelSerializer``

        Returns
        -------
        float
            The workshop average session's score

        """

        return instance.get_training_session_average_score(
            self.context['training_session']
        )

    def get_maximum_score(self, instance):
        """Get the maximum possible score for this workshop

        For the parameters, see ``rest_framework.serializers.ModelSerializer``

        Returns
        -------
        int
            The workshop maximum possible score

        """

        return instance.max_points_yield
