"""This module contains the test cases for the view of the ``questionnaires`` app"""

import json

from ..factories import (
    LiveQuizFactory,
    ChoicesQuestionFactory,
)
from ..models import (
    ChoicesQuestion,
    LivePolling,
    LiveQuiz,
    Polling,
    Quiz,
)

from .test_cases import QuestionnairesTestCase


class QuestionnaireViewsTestCase(QuestionnairesTestCase):
    """Test case for the view related to the ``Questionnaire`` subclasses"""

    @classmethod
    def setUpTestData(cls):
        """Build a set of data to submit and store the urls to test"""

        super().setUpTestData()

        # We cannot edit the quiz if the training is not in ``DRAFT`` state
        cls.training.version_state = cls.training.VERSION_STATES.DRAFT
        cls.training.save()

        cls.questionnaire_data = {
            'name': 'New name',
            'description': 'New description',
            'is_score_displayed': True,
            'is_questions_order_random': True,
            'are_questions_mandatory': True
        }

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

        super().setUp()

        self.login_as_trainer()

    def test_quiz_create_url_should_work_with_training(self):
        """Test that we should be able to create a quiz"""

        response = self.client.post(
            Quiz.get_training_create_url(self.training),
            self.questionnaire_data
        )

        self.assertEqual(response.status_code, 200)

        # We get a new quiz
        quiz = Quiz.objects.get(
            name=self.questionnaire_data['name']
        )
        # We got the right success_url in the response
        self.assertEqual(
            json.loads(response.content.decode('utf-8')),
            {
                'valid': True,
                'success_url': quiz.get_detail_url()
            }
        )

        # With correct attributes
        self.assertEqual(
            quiz.description,
            self.questionnaire_data['description']
        )
        self.assertEqual(
            quiz.is_score_displayed,
            self.questionnaire_data['is_score_displayed']
        )
        self.assertEqual(
            quiz.is_questions_order_random,
            self.questionnaire_data['is_questions_order_random']
        )
        self.assertEqual(
            quiz.are_questions_mandatory,
            self.questionnaire_data['are_questions_mandatory']
        )

    def test_quiz_create_url_should_work(self):
        """Test that we should be able to create a quiz"""

        response = self.client.post(
            Quiz.get_create_url(),
            self.questionnaire_data
        )

        self.assertEqual(response.status_code, 200)

        # We get a new quiz
        quiz = Quiz.objects.get(
            name=self.questionnaire_data['name']
        )

        # We got the right success_url in the response
        self.assertEqual(
            json.loads(response.content.decode('utf-8')),
            {
                'valid': True,
                'success_url': quiz.get_detail_url()
            }
        )

        # With correct attributes
        self.assertEqual(
            quiz.description,
            self.questionnaire_data['description']
        )
        self.assertEqual(
            quiz.is_score_displayed,
            self.questionnaire_data['is_score_displayed']
        )
        self.assertEqual(
            quiz.is_questions_order_random,
            self.questionnaire_data['is_questions_order_random']
        )
        self.assertEqual(
            quiz.are_questions_mandatory,
            self.questionnaire_data['are_questions_mandatory']
        )

    def test_livequiz_create_url_should_work_with_training(self):
        """Test that we should be able to create a live quiz"""

        response = self.client.post(
            LiveQuiz.get_training_create_url(self.training),
            self.questionnaire_data
        )

        self.assertEqual(response.status_code, 200)

        # We get a new live quiz
        live_quiz = LiveQuiz.objects.get(
            name=self.questionnaire_data['name']
        )

        # We got the right success_url in the response
        self.assertEqual(
            json.loads(response.content.decode('utf-8')),
            {
                'valid': True,
                'success_url': live_quiz.get_detail_url()
            }
        )

        # With correct attributes
        self.assertEqual(
            live_quiz.description,
            self.questionnaire_data['description']
        )
        self.assertEqual(
            live_quiz.is_score_displayed,
            self.questionnaire_data['is_score_displayed']
        )

    def test_livequiz_create_url_should_work(self):
        """Test that we should be able to create a live quiz"""

        response = self.client.post(
            LiveQuiz.get_create_url(),
            self.questionnaire_data
        )

        self.assertEqual(response.status_code, 200)

        # We get a new live quiz
        live_quiz = LiveQuiz.objects.get(
            name=self.questionnaire_data['name']
        )

        # We got the right success_url in the response
        self.assertEqual(
            json.loads(response.content.decode('utf-8')),
            {
                'valid': True,
                'success_url': live_quiz.get_detail_url()
            }
        )

        # With correct attributes
        self.assertEqual(
            live_quiz.description,
            self.questionnaire_data['description']
        )
        self.assertEqual(
            live_quiz.is_score_displayed,
            self.questionnaire_data['is_score_displayed']
        )

    def test_polling_create_url_should_work_with_training(self):
        """Test that we should be able to create a polling"""

        response = self.client.post(
            Polling.get_training_create_url(self.training),
            self.questionnaire_data
        )

        self.assertEqual(response.status_code, 200)

        # We get a new polling
        polling = Polling.objects.get(
            name=self.questionnaire_data['name']
        )

        # We got the right success_url in the response
        self.assertEqual(
            json.loads(response.content.decode('utf-8')),
            {
                'valid': True,
                'success_url': polling.get_detail_url()
            }
        )

        # With correct attributes
        self.assertEqual(
            polling.description,
            self.questionnaire_data['description']
        )
        self.assertEqual(
            polling.are_questions_mandatory,
            self.questionnaire_data['are_questions_mandatory']
        )

    def test_polling_create_url_should_work(self):
        """Test that we should be able to create a polling"""

        response = self.client.post(
            Polling.get_create_url(),
            self.questionnaire_data
        )

        self.assertEqual(response.status_code, 200)

        # We get a new polling
        polling = Polling.objects.get(
            name=self.questionnaire_data['name']
        )

        # We got the right success_url in the response
        self.assertEqual(
            json.loads(response.content.decode('utf-8')),
            {
                'valid': True,
                'success_url': polling.get_detail_url()
            }
        )

        # With correct attributes
        self.assertEqual(
            polling.description,
            self.questionnaire_data['description']
        )
        self.assertEqual(
            polling.are_questions_mandatory,
            self.questionnaire_data['are_questions_mandatory']
        )

    def test_livepolling_create_url_should_work_with_training(self):
        """Test that we should be able to create a live polling"""

        response = self.client.post(
            LivePolling.get_training_create_url(self.training),
            self.questionnaire_data
        )

        self.assertEqual(response.status_code, 200)

        # We get a new live polling
        live_polling = LivePolling.objects.get(
            name=self.questionnaire_data['name']
        )

        # We got the right success_url in the response
        self.assertEqual(
            json.loads(response.content.decode('utf-8')),
            {
                'valid': True,
                'success_url': live_polling.get_detail_url()
            }
        )

        # With correct attributes
        self.assertEqual(
            live_polling.description,
            self.questionnaire_data['description']
        )

    def test_livepolling_create_url_should_work(self):
        """Test that we should be able to create a live polling"""

        response = self.client.post(
            LivePolling.get_create_url(),
            self.questionnaire_data
        )

        self.assertEqual(response.status_code, 200)

        # We get a new live polling
        live_polling = LivePolling.objects.get(
            name=self.questionnaire_data['name']
        )

        # We got the right success_url in the response
        self.assertEqual(
            json.loads(response.content.decode('utf-8')),
            {
                'valid': True,
                'success_url': live_polling.get_detail_url()
            }
        )

        # With correct attributes
        self.assertEqual(
            live_polling.description,
            self.questionnaire_data['description']
        )

    def test_questionnaire_update_should_work(self):
        """Test that we should be able to update a ``Questionnaire`` subclass

        We only test this view with a ``Quiz``, since the other views are on the same
        url and only the ``model`` attribute change.

        """

        current_name = self.quiz.name

        response = self.client.post(
            self.quiz.get_edit_url(),
            self.questionnaire_data
        )

        self.assertEqual(response.status_code, 200)

        self.quiz.refresh_from_db()

        self.assertNotEqual(self.quiz.name, current_name)
        self.assertEqual(self.quiz.name, self.questionnaire_data['name'])


class QuestionViewsTestCase(QuestionnairesTestCase):
    """Test case for the views related to the ``Questions`` subclasses"""

    @classmethod
    def setUpTestData(cls):
        """Store the data to submit to the forms"""

        super().setUpTestData()

        # We cannot edit the quiz if the training is not in ``DRAFT`` state
        cls.training.version_state = cls.training.VERSION_STATES.DRAFT
        cls.training.save()

        cls.create_url = ChoicesQuestion.get_create_url(questionnaire=cls.quiz)

        cls.formset_prefix = 'questionnaires-choice-question_content_type-question_id'

        # Data to create a new question
        cls.formset_create_data = {
            'label': 'New question',
            'is_multiple_choices': True,
            'points': 1,
            # answers meta-data
            '%s-TOTAL_FORMS' % cls.formset_prefix: 1,
            '%s-INITIAL_FORMS' % cls.formset_prefix: 0,
            '%s-MIN_NUM_FORMS' % cls.formset_prefix: 1,
            '%s-MAX_NUM_FORMS' % cls.formset_prefix: 10,
        }

        cls.first_question = cls.choices_questions[0]

        # Data to update an existing question
        cls.formset_update_data = {
            # question
            'label': cls.first_question.label,
            'is_multiple_choices': cls.first_question.is_multiple_choices,
            'points': cls.first_question.points,
            # choices meta-data
            '%s-TOTAL_FORMS' % cls.formset_prefix: cls.first_question.choices.count(),
            '%s-INITIAL_FORMS' % cls.formset_prefix: cls.first_question.choices.count(),
            '%s-MIN_NUM_FORMS' % cls.formset_prefix: 1,
            '%s-MAX_NUM_FORMS' % cls.formset_prefix: 10,
        }

        # choices data
        for index, choice in enumerate(cls.first_question.choices.all()):
            cls.formset_update_data.update({
                '%s-%s-id' % (cls.formset_prefix, index,): choice.pk,
                '%s-%s-order' % (cls.formset_prefix, index,): index + 1,
                '%s-%s-label' % (cls.formset_prefix, index,): choice.label,
                '%s-%s-is_valid' % (cls.formset_prefix, index,): choice.is_valid,
                '%s-%s-question' % (cls.formset_prefix, index,): cls.first_question.pk,
            })

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

        super().setUp()

        self.login_as_trainer()

    def test_questions_delete_view_should_allow_us_to_soft_delete_multiple_questions(self):
        """Test that the questions delete view should allow us to delete multiple questions"""

        self.login_as_trainer()

        # At first, we got 6 non-deleted questions, and 6 questions in total
        self.assertEqual(self.quiz.questions.non_deleted().count(), 6)
        self.assertEqual(self.quiz.questions.count(), 6)

        # We delete the first question
        response = self.client.post(
            self.quiz.get_questions_delete_url(),
            {
                'questions': [self.quiz.questions.first().id]
            }
        )

        # We should be redirected to the quiz detail url
        self.assertRedirects(response, self.quiz.get_absolute_url())

        # We should have 5 non-deleted questions and 6 questions in total
        self.assertEqual(self.quiz.questions.non_deleted().count(), 5)
        self.assertEqual(self.quiz.questions.count(), 6)

        # Now, we delete two more questions
        response = self.client.post(
            self.quiz.get_questions_delete_url(),
            {
                'questions': [
                    self.quiz.questions.all()[1].id,
                    self.quiz.questions.all()[2].id
                ]
            }
        )

        # We should be redirected to the quiz detail url
        self.assertRedirects(response, self.quiz.get_absolute_url())

        # We should have 3 non-deleted questions and 6 questions in total
        self.assertEqual(self.quiz.questions.non_deleted().count(), 3)
        self.assertEqual(self.quiz.questions.count(), 6)

    def test_create_view_should_create_a_new_question_and_choices(self):
        """Test that the create view should allow us to create a new question and choices"""

        self.formset_create_data['%s-TOTAL_FORMS' % self.formset_prefix] = 2

        # Data for the choice
        self.formset_create_data.update({
            '%s-0-order' % self.formset_prefix: 0,
            '%s-0-label' % self.formset_prefix: 'test label 1',
            '%s-0-is_valid' % self.formset_prefix: True,
            '%s-1-order' % self.formset_prefix: 1,
            '%s-1-label' % self.formset_prefix: 'test label 2',
            '%s-1-is_valid' % self.formset_prefix: False,
        })

        response = self.client.post(self.create_url, self.formset_create_data)
        self.assertTrue(response.status_code, 200)

        # Our question was created
        question = ChoicesQuestion.objects.get(label='New question')

        # this question got two choices
        self.assertEqual(question.choices.count(), 2)

        choice_1 = question.choices.get(label='test label 1')
        self.assertEqual(choice_1.is_valid, True)

        choice_2 = question.choices.get(label='test label 2')
        self.assertEqual(choice_2.is_valid, False)

    def test_update_view_should_update_the_question_and_the_choices(self):
        """Test that the update view should allow us to update the question and the choices"""

        # We update the label of the question
        self.formset_update_data['label'] = 'Updated label'

        # And the label of the first choice
        self.formset_update_data['%s-0-label' % self.formset_prefix] = 'Updated label'

        response = self.client.post(
            self.first_question.get_edit_url(),
            self.formset_update_data
        )

        # We should be redirected to the details
        self.assertRedirects(response, self.quiz.get_detail_url())

        self.first_question.refresh_from_db()
        self.assertEqual(self.first_question.label, 'Updated label')

        first_choice = self.first_question.choices.first()
        self.assertEqual(first_choice.label, 'Updated label')

    def test_update_view_should_allow_adding_new_choice(self):
        """Test that the update view should allow us to add a new choice"""

        # At first, we got 5 choices on the question
        self.assertEqual(self.first_question.choices.count(), 5)

        formset_update_data = self.formset_update_data.copy()

        formset_update_data['%s-TOTAL_FORMS' % self.formset_prefix] = 6
        formset_update_data.update({
            '%s-5-order' % self.formset_prefix: 0,
            '%s-5-label' % self.formset_prefix: 'test label',
            '%s-5-is_valid' % self.formset_prefix: True,
            '%s-5-question' % self.formset_prefix: self.first_question.pk,
        })

        response = self.client.post(
            self.first_question.get_edit_url(),
            formset_update_data
        )

        self.assertRedirects(response, self.quiz.get_detail_url())

        # We should now have a sixth choice, the one we just created
        self.assertEqual(self.first_question.choices.count(), 6)

    def test_update_view_should_allow_soft_delete_choice(self):
        """Test that the update view should allow us to soft-delete a choice"""

        # We got 5 non-deleted choices
        self.assertEqual(self.first_question.choices.non_deleted().count(), 5)

        # Add post data to set the first choice as deleted
        self.formset_update_data['%s-0-DELETE' % self.formset_prefix] = True

        response = self.client.post(
            self.first_question.get_edit_url(),
            self.formset_update_data
        )

        self.assertRedirects(response, self.quiz.get_detail_url())

        # We got 4 non-deleted choices
        self.assertEqual(self.first_question.choices.non_deleted().count(), 4)

        # But still a total of 5 choices
        self.assertEqual(self.first_question.choices.count(), 5)


class LiveQuestionnaireQuestionViewsTestCase(QuestionViewsTestCase):
    """Test case for the views related to the ``Questions`` subclasses on a "live" questionnaire"""

    @classmethod
    def setUpTestData(cls):
        """Create a new "live" questionnaire and a question on it"""

        super().setUpTestData()

        # Create a "live" questionnaire
        cls.live_quiz = LiveQuizFactory(training=cls.training)

        # Add a new choice question on it
        cls.choices_question = ChoicesQuestionFactory(questionnaire=cls.live_quiz)

    def test_live_questionnaire_should_allow_only_five_choices(self):
        """Test that a "live" questionnaire should allow only 5 choices"""

        response = self.client.get(self.choices_question.get_edit_url())

        # We should have the correct max number of choices set in the formset
        self.assertEqual(
            response.context['choices_formset'].management_form['MAX_NUM_FORMS'].value(),
            5
        )

    def test_live_questionnaire_should_set_max_length_on_choice_label(self):
        """Test that the "label" field "maxlength" on a choice in a "live" questionnaire"""

        response = self.client.get(self.choices_question.get_edit_url())

        # The label field on the choices of a question linked to "live" questionnaire should
        # be 140 chars max
        self.assertEqual(
            response.context['choices_formset'][0]['label'].field.widget.attrs['maxlength'],
            140
        )

    def test_live_questionnaire_should_set_max_length_on_question_label(self):
        """Test that the "label" field "maxlength" on a question in a "live" questionnaire"""

        response = self.client.get(self.choices_question.get_edit_url())

        # The label field on the question linked to "live" questionnaire should be 140 chars max
        self.assertEqual(
            response.context['form']['label'].field.widget.attrs['maxlength'],
            140
        )
