"""This module holds the api v2 related views test cases for the ``questionnaires`` app"""

from django.core.urlresolvers import reverse

from rest_framework import status

from mtp.apps.api.tests.test_cases import MTPAPITestCase
from mtp.apps.questionnaires.factories import (
    ChoicesQuestionFactory,
    OpenQuestionFactory,
)
from mtp.apps.questionnaires.models import (
    ChoicesAnswer,
    OpenAnswer,
)
from mtp.apps.questionnaires.tests.test_cases import QuestionnairesTestCase
from mtp.apps.trainings.factories import TrainingSessionFactory

from ..serializers import (
    QuizSerializer,
)


class QuestionnairesAPIViewsTestCase(QuestionnairesTestCase, MTPAPITestCase):
    """Test case for the api views related to the ``Questionnaires``subclasses"""

    def setUp(self):
        """Login as a trainee"""

        super().setUp()

        self.login_as_trainee()

    def test_get_single_quiz_should_work(self):
        """Test that we should be able to query a single ``Quiz``"""

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

        # 25 queries, because polymorphic needed to load the content types
        with self.assertNumQueries(25):
            response = self.client.get(url)

        self.assertEqual(response.status_code, status.HTTP_200_OK)

        self.assertDictEqual(
            response.data,
            QuizSerializer(self.quiz).data
        )

        # Add another question to this quiz
        ChoicesQuestionFactory(questionnaire=self.quiz)

        # Now 21 queries, the content types are loaded into the cache
        with self.assertNumQueries(21):
            self.client.get(url)

    def test_create_open_answer_should_work(self):
        """Test that we should be able to create an open answer"""

        open_question = OpenQuestionFactory(questionnaire=self.quiz)

        url = reverse('apiv2:openanswer-list')

        # We got 0 open answer for this session / user / trainee / question
        self.assertFalse(
            OpenAnswer.objects.filter(
                question=open_question,
                session=self.training_session,
                user=self.trainee
            ).exists()
        )

        self.client.post(
            url,
            {
                'content': 'New open answer content',
                'question': open_question.id,
                'session': self.training_session.id,
                'user': self.trainee.id
            }
        )

        # We should have one new open answer
        open_answer = OpenAnswer.objects.get(
            question=open_question,
            session=self.training_session,
            user=self.trainee
        )

        self.assertEqual(open_answer.content, 'New open answer content')

        # Save the open answer with a new content
        self.client.post(
            url,
            {
                'content': 'Different open answer content',
                'question': open_question.id,
                'session': self.training_session.id,
                'user': self.trainee.id
            }
        )

        open_answer.refresh_from_db()

        self.assertEqual(open_answer.content, 'Different open answer content')

        # We should be able to save the open answer with an empty content
        response = self.client.post(
            url,
            {
                'content': '',
                'question': open_question.id,
                'session': self.training_session.id,
                'user': self.trainee.id
            }
        )

        self.assertEqual(response.status_code, 201)

        open_answer.refresh_from_db()

        self.assertEqual(open_answer.content, '')

    def test_create_choices_answer_should_work(self):
        """Test that we should be able to create a choices answer"""

        first_question = self.choices_questions[0]

        first_choice = first_question.choices.first()
        second_choice = first_question.choices.last()

        url = reverse('apiv2:choicesanswer-list')

        # We got 0 choices answer for this session / user / trainee / question
        self.assertFalse(
            ChoicesAnswer.objects.filter(
                question=first_question,
                session=self.training_session,
                user=self.trainee
            ).exists()
        )

        self.client.post(
            url,
            {
                'choices': [
                    first_choice.id,
                    second_choice.id
                ],
                'question': first_question.id,
                'session': self.training_session.id,
                'user': self.trainee.id
            }
        )

        # We should have one new choices answer...
        choice_answer = ChoicesAnswer.objects.get(
            question=first_question,
            session=self.training_session,
            user=self.trainee
        )

        # ...with the correct choices
        self.assertCountEqual(
            list(choice_answer.choices.all()),
            [
                first_choice,
                second_choice
            ]
        )

        # We can change the choices
        self.client.post(
            url,
            {
                'choices': [
                    first_choice.id
                ],
                'question': first_question.id,
                'session': self.training_session.id,
                'user': self.trainee.id
            }
        )

        choice_answer.refresh_from_db()

        self.assertCountEqual(
            list(choice_answer.choices.all()),
            [
                first_choice
            ]
        )

        # Or delete all the choices
        self.client.post(
            url,
            {
                'choices': [],
                'question': first_question.id,
                'session': self.training_session.id,
                'user': self.trainee.id
            }
        )

        choice_answer.refresh_from_db()

        self.assertEqual(
            list(choice_answer.choices.all()),
            []
        )

    def test_list_answer_should_work(self):
        """Test that the list answer endpoint should work"""

        url = reverse('apiv2:answer-list', kwargs={
            'questionnaire_uuid': self.quiz.id,
        })

        # At first, no answers
        response = self.client.get(url, {
            'session': self.training_session.id,
            'user': self.trainee.id
        })

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data, [])

        # Add a choice answer
        first_question = self.choices_questions[0]
        first_choice_id = first_question.choices.first().id

        choice_answer = first_question.create_or_update_user_answer(
            session=self.training_session,
            user=self.trainee,
            choices=[first_choice_id]
        )

        response = self.client.get(url, {
            'session': self.training_session.id,
            'user': self.trainee.id
        })

        self.assertCountEqual(
            response.data,
            [
                choice_answer.serializer_class(choice_answer).data
            ]
        )

        # Add an open answer
        open_question = OpenQuestionFactory(questionnaire=self.quiz)
        open_answer = open_question.create_or_update_user_answer(
            session=self.training_session,
            user=self.trainee,
            content='test'
        )

        response = self.client.get(url, {
            'session': self.training_session.id,
            'user': self.trainee.id
        })

        self.assertCountEqual(
            response.data,
            [
                choice_answer.serializer_class(choice_answer).data,
                open_answer.serializer_class(open_answer).data
            ]
        )

    def test_list_answer_filter_per_session_should_work(self):
        """Test that the list answer endpoint filter per session should work"""

        url = reverse('apiv2:answer-list', kwargs={
            'questionnaire_uuid': self.quiz.id,
        })

        new_training_session = TrainingSessionFactory()

        # At first, we don't have any answers for the current session...
        response = self.client.get(url, {
            'session': self.training_session.id
        })
        self.assertEqual(len(response.data), 0)

        # ...and also no answers for our other training session
        response = self.client.get(url, {
            'session': new_training_session.id
        })
        self.assertEqual(len(response.data), 0)

        # Add a choice answer on the first session
        first_question = self.choices_questions[0]
        first_choice_id = first_question.choices.first().id
        first_question.create_or_update_user_answer(
            session=self.training_session,
            user=self.trainee,
            choices=[first_choice_id]
        )

        # Now we have one new answer on the first session...
        response = self.client.get(url, {
            'session': self.training_session.id
        })
        self.assertEqual(len(response.data), 1)

        # ...but still 0 on our other session
        response = self.client.get(url, {
            'session': new_training_session.id
        })
        self.assertEqual(len(response.data), 0)

    def test_list_answer_filter_per_user_should_work(self):
        """Test that the list answer endpoint filter per user should work"""

        url = reverse('apiv2:answer-list', kwargs={
            'questionnaire_uuid': self.quiz.id,
        })

        # At first, we don't have any answers for the current session
        response = self.client.get(url, {
            'session': self.training_session.id
        })
        self.assertEqual(len(response.data), 0)

        # New answer for our first trainee
        first_question = self.choices_questions[0]
        first_choice_id = first_question.choices.first().id
        self.choices_questions[0].create_or_update_user_answer(
            session=self.training_session,
            user=self.trainee,
            choices=[first_choice_id]
        )

        # Now we got one answer in the session...
        response = self.client.get(url, {
            'session': self.training_session.id
        })
        self.assertEqual(len(response.data), 1)

        # And we should be able to get the answer also with the user filter
        response = self.client.get(url, {
            'session': self.training_session.id,
            'user': self.trainee.id,
        })
        self.assertEqual(len(response.data), 1)

        # If we filter with another trainee, no answers
        response = self.client.get(url, {
            'session': self.training_session.id,
            'user': self.trainees[1].id,
        })
        self.assertEqual(len(response.data), 0)
