"""This module holds the tests for the serializers of the ``apiv2`` module of ``assessments`` app"""

from django.test import override_settings

from django_lockable.models import Lockable
from django_lockable.utils import _no_fields_locking

from mtp.apps.accounts.apiv2.serializers import MTPUserSerializer
from mtp.apps.assessments.models import (
    AssessableActivityUserScore,
    CertificationProgramUserStatus,
    CompetencyTree,
    CompetencyUserScore,
    DimensionUserScore,
    LessonReference,
    UserLessonRecommendation,
)
from mtp.apps.assessments.tests.test_cases import MTPComplexAssessmentTestCase
from mtp.apps.workshops.models import Workshop

from ..serializers import (
    UserSessionAssessmentScoresSerializer,
    UserLessonRecommendationSerializer,
)


class UserSessionAssessmentScoresSerializerErrorTestCase(MTPComplexAssessmentTestCase):
    """Test case w/o proper needed data to test the error handling

    See ``UserSessionAssessmentScoresSerializerTestCase`` for the "real" tests.

    """

    def test_serialize_session_score_error(self):
        """Tests for the ``UserSessionAssessmentScoresSerializer`` serializer w/ error"""

        # we need a closed session
        with override_settings(INSTANCE_IS_MASTER=True):
            self.training_session.close()

        test_user = self.training_session.users.non_deleted().first()

        with self.assertRaises(CertificationProgramUserStatus.DoesNotExist):
            UserSessionAssessmentScoresSerializer(
                self.training_session,
                context={
                    'user': test_user,
                },
            )


class BaseAssessmentScoresTestCase(MTPComplexAssessmentTestCase):
    """Base TestCase for our AssessmentScores views"""

    @classmethod
    def setUpClass(cls):
        """We change users in training session."""

        super().setUpClass()

        # We use only ``cls.trainee`` in training session
        # because our ``_record_*`` methods works only with him.
        cls.training_session.users.clear()
        cls.training_session.users.add(cls.trainee)

    @classmethod
    def setUpTestData(cls):
        """We create several scores to have some test data"""

        super().setUpTestData()

        cls._record_dimension_scores([69, 65, 80, 98, 91, 77])
        cls._record_competency_scores([70, 89])
        cls._record_global_score(75)
        cls._compute_certification_status()
        cls._compute_recommendations()


class UserSessionAssessmentScoresSerializerTestCase(BaseAssessmentScoresTestCase):
    """Tests for the ``UserSessionAssessmentScoresSerializer`` serializer"""

    def test_serialize_session_score(self):
        """Test retrieving a valid session scores"""

        # we need a closed session
        with override_settings(INSTANCE_IS_MASTER=True):
            self.training_session.close()

        # make one of the activities of the training non assessing
        # to verify later it's filtered out in ``activity_scores``
        with _no_fields_locking(Lockable):
            na_activity = self.training_session.training.activities.instance_of(Workshop).first()
            na_activity.is_assessing = False
            na_activity.save()
        self.training_session.refresh_from_db()

        test_user = self.training_session.users.non_deleted().first()

        serialized_data = UserSessionAssessmentScoresSerializer(
            self.training_session,
            context={
                'user': test_user,
            },
        ).data

        certification = CertificationProgramUserStatus.objects.non_deleted().get(
            user=test_user,
            session=self.training_session,
        )

        competency_tree = CompetencyTree.objects.get_for_session(self.training_session)

        expected_data = {
            'session_id': self.training_session.pk,
            'user': MTPUserSerializer(test_user).data,
            'date_reached': certification.date_reached,
            'is_forced': certification.is_forced,
            'is_validated': certification.is_validated,
            'status': certification.status.name if certification.status else None,
            'competency_scores': [
                {
                    'name': competency.name,
                    'id': competency.pk,
                    'original_id': competency.original_version_id,
                    'score': CompetencyUserScore.objects.get(
                        user=test_user,
                        competency=competency,
                        session=self.training_session,
                    ).score,
                    'dimension_scores': [
                        {
                            'name': dimension.name,
                            'id': dimension.pk,
                            'original_id': dimension.original_version_id,
                            'score': DimensionUserScore.objects.get(
                                user=test_user,
                                dimension=dimension,
                                session=self.training_session,
                            ).score,
                        } for dimension in competency.dimensions.non_deleted()
                    ],
                } for competency in competency_tree.competencies.non_deleted()
            ],
            'activity_scores': [
                {
                    'name': activity.name,
                    'id': activity.pk,
                    'original_id': activity.original_version_id,
                    'score': AssessableActivityUserScore.objects.get(
                        user=test_user,
                        activity_id=activity.pk,
                        session=self.training_session,
                    ).score,
                } for activity in self.training_session.training.activities.filter(
                    is_assessing=True
                ).non_deleted()
            ],
        }

        self.assertDictEqual(serialized_data, expected_data)


class UserLessonRecommendationSerializerTestCase(BaseAssessmentScoresTestCase):
    """Test the ``UserLessonRecommendationSerializer`` serializer"""

    @classmethod
    def setUpTestData(cls):
        """Setup a recommendation with a reference for our tests"""

        super().setUpTestData()

        cls.recommendation = UserLessonRecommendation.objects.first()

        # let's attach a reference for completeness sake
        reference = LessonReference(
            external_name='XXX',
            external_id='XXX',
        )
        reference.save()
        cls.recommendation.lesson.reference = reference
        cls.recommendation.lesson.save()

    def test_serialize_recommendation(self):
        """Test the serializer output"""

        serializer = UserLessonRecommendationSerializer(self.recommendation)

        expected_data = {
            'status': self.recommendation.level.pk,
            'lesson': {
                'name': self.recommendation.lesson.name,
                'type': self.recommendation.lesson.type,
                'topic_name': self.recommendation.lesson.topic.name,
                'reference': {
                    'name': 'XXX',
                    'external_id': 'XXX',
                },
                'recommendation_levels': [
                    {
                        'id': rlevel.pk,
                        'name': rlevel.name,
                    } for rlevel in self.recommendation.lesson.recommendation_levels.non_deleted()
                ],
            },
        }

        self.assertDictEqual(serializer.data, expected_data)
