"""This module contains the apiv2-related views of the `èvaluations`` app"""

import json

from django.core.cache import cache
from django.core.urlresolvers import reverse

from mtp.apps.api.tests.test_cases import MTPAPITestCase
from mtp.apps.evaluations.factories import RatingFactory
from mtp.apps.evaluations.factories import EvaluationFactory
from mtp.apps.evaluations.models import Criterion, Rating
from mtp.apps.evaluations.tests.test_cases import EvaluationTestCase

from ..serializers import EvaluationSerializer


class EvaluationAPIEndpointsTestCase(MTPAPITestCase, EvaluationTestCase):
    """Test case for the api views related to the ``Evaluation`` model"""

    def test_retrieve_evaluation_endpoint_should_work(self):
        """Test that the detail view of the evaluation should work"""

        cache.clear()

        url = reverse('apiv2:evaluation-detail', kwargs={
            'pk': self.evaluation.pk
        })

        self.login_as_trainee()

        # First time, 46 queries (!?)
        with self.assertNumQueries(46):
            response = self.client.get(url)

        self.assertEqual(response.status_code, 200)

        self.assertEqual(
            response.data,
            EvaluationSerializer(self.evaluation).data
        )

        # Second time, only 4 queries :)
        with self.assertNumQueries(4):
            self.client.get(url)

        # Test with a brand new evaluation (with a published training)
        new_evaluation = EvaluationFactory()
        url = reverse('apiv2:evaluation-detail', kwargs={
            'pk': new_evaluation.pk
        })
        new_evaluation.training.publish()

        # 6 queries the first time (our evaluation don't have any exercises)
        with self.assertNumQueries(6):
            response = self.client.get(url)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(
            response.data,
            EvaluationSerializer(new_evaluation).data
        )

        # 4 queries the second time
        with self.assertNumQueries(4):
            response = self.client.get(url)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(
            response.data,
            EvaluationSerializer(new_evaluation).data
        )


class EvaluationRatingViewTestCase(MTPAPITestCase, EvaluationTestCase):
    """Test case for the api views related to the ``Rating`` model"""

    @classmethod
    def setUpClass(cls):
        """Build the urls and store the first exercise / criterion"""

        super().setUpClass()

        cls.list_ratings_url = reverse('apiv2:evaluation-rating-list', kwargs={
            'evaluation_uuid': cls.evaluation.pk,
            'session_uuid': cls.training_session.pk
        })

        cls.exercise = cls.evaluation.exercises.first()
        cls.qualitative_criterion = cls.exercise.criteria.filter(
            criterion_type=Criterion.CRITERION_TYPES.QUALITATIVE
        ).first()
        cls.quantitative_criterion = cls.exercise.criteria.filter(
            criterion_type=Criterion.CRITERION_TYPES.QUANTITATIVE
        ).first()

        cls.quantitative_rating_dict = {
            'criterion': cls.quantitative_criterion.id,
            'trainee': cls.trainees[1].id,
            'score': 1
        }
        cls.qualitative_rating_dict = {
            'criterion': cls.qualitative_criterion.id,
            'trainee': cls.trainees[1].id,
            'text': 'test'
        }

    def test_list_ratings_should_work(self):
        """Test that the rating list should work"""

        self.login_as_trainee()
        response = self.client.get(self.list_ratings_url)

        self.assertEqual(response.status_code, 200)

        # No ratings at the moment
        self.assertEqual(
            len(response.data),
            0
        )

        # Add a new rating
        RatingFactory(
            criterion=self.qualitative_criterion,
            judge=self.trainee,
            trainee=self.trainees[1],
            session=self.training_session
        )

        # Got one rating now
        response = self.client.get(self.list_ratings_url)
        self.assertEqual(
            len(response.data),
            1
        )

    def test_save_single_quantitative_rating_should_work(self):
        """Test that saving a single quantitative rating should work"""

        self.login_as_trainee()

        response = self.client.post(
            self.list_ratings_url,
            json.dumps(self.quantitative_rating_dict),
            content_type='application/json'
        )

        self.assertEqual(response.status_code, 201)

        self.assertEqual(
            Rating.objects.filter(criterion=self.quantitative_criterion).count(),
            1
        )

        # The "judge" and "session" attributes are corrects
        self.assertEqual(
            Rating.objects.get(id=response.data['id']).judge,
            self.trainee
        )
        self.assertEqual(
            Rating.objects.get(id=response.data['id']).session,
            self.training_session
        )

    def test_save_single_qualitative_rating_should_work(self):
        """Test that saving a single qualitative rating should work"""

        self.login_as_trainee()

        response = self.client.post(
            self.list_ratings_url,
            json.dumps(self.qualitative_rating_dict),
            content_type='application/json'
        )

        self.assertEqual(response.status_code, 201)

        self.assertEqual(
            Rating.objects.filter(criterion=self.qualitative_criterion).count(),
            1
        )

        # The "judge" and "session" attributes are corrects
        self.assertEqual(
            Rating.objects.get(id=response.data['id']).judge,
            self.trainee
        )
        self.assertEqual(
            Rating.objects.get(id=response.data['id']).session,
            self.training_session
        )

    def test_save_multiple_ratings_should_work(self):
        """Test that saving multiple ratings should work"""

        self.login_as_trainee()

        response = self.client.post(
            self.list_ratings_url,
            json.dumps([
                self.qualitative_rating_dict,
                self.quantitative_rating_dict,
            ]),
            content_type='application/json'
        )

        self.assertEqual(response.status_code, 201)
        self.assertEqual(
            len(response.data),
            2
        )

        self.assertEqual(
            Rating.objects.filter(criterion=self.qualitative_criterion).count(),
            1
        )
        self.assertEqual(
            Rating.objects.filter(criterion=self.quantitative_criterion).count(),
            1
        )

    def test_update_text_on_existing_rating_should_update_it(self):
        """Test that saving an already existing rating should update it"""

        # New qualitative rating
        rating = RatingFactory(
            criterion=self.qualitative_criterion,
            judge=self.trainee,
            trainee=self.trainees[1],
            session=self.training_session
        )

        initial_text = rating.text

        self.login_as_trainee()

        response = self.client.post(
            self.list_ratings_url, json.dumps({
                'id': rating.id,
                'criterion': rating.criterion.id,
                'trainee': rating.trainee.id,
                'text': 'test'
            }),
            content_type='application/json'
        )

        self.assertEqual(response.status_code, 201)

        # The text of the rating should be updated
        rating.refresh_from_db()
        self.assertNotEqual(rating.text, initial_text)
        self.assertEqual(rating.text, 'test')

    def test_update_score_on_existing_rating_should_update_it(self):
        """Test that saving an already existing rating should update it"""

        # New quantitative rating
        rating = RatingFactory(
            criterion=self.quantitative_criterion,
            judge=self.trainee,
            trainee=self.trainees[1],
            session=self.training_session
        )

        initial_score = rating.score

        self.login_as_trainee()

        response = self.client.post(
            self.list_ratings_url, json.dumps({
                'id': rating.id,
                'criterion': rating.criterion.id,
                'trainee': rating.trainee.id,
                'score': 99
            }),
            content_type='application/json'
        )

        self.assertEqual(response.status_code, 201)

        # The score of the rating should be updated
        rating.refresh_from_db()
        self.assertNotEqual(rating.score, initial_score)
        self.assertEqual(rating.score, 99)
