"""Test cases for the ``workshops`` views."""

from django.core.urlresolvers import reverse

from ..factories import WorkshopStepImageFactory
from ..models import Workshop, WorkshopStepQuestion

from .test_cases import WorkshopTestCase

class WorkshopViewsTestCase(WorkshopTestCase):
    """Test case that tests the ``Workshop`` related views."""

    @classmethod
    def setUpTestData(cls):
        super().setUpTestData()

        cls.urls_to_test = [
            Workshop.get_create_url(cls.training),
            cls.workshop.get_detail_url(),
            cls.workshop.get_edit_url(),
            cls.workshop.get_delete_url(),
        ]

        cls.training.version_state = cls.training.VERSION_STATES.DRAFT
        cls.training.save()

    def test_non_training_owner_should_not_have_access(self):
        """Test that users who don't own the training should not be allowed to access the views"""

        self.login_as_admin()

        for url in self.urls_to_test:
            with self.subTest(url=url):
                response = self.client.get(url)
                self.assertEqual(response.status_code, 404)

    def test_training_owner_should_have_access(self):
        """Test that users who own the training should be allowed to access the views"""

        self.login_as_trainer()
        for url in self.urls_to_test:
            with self.subTest(url=url):
                response = self.client.get(url)
                self.assertEqual(response.status_code, 200)

class WorkshopStepQuestionViewsTestCase(WorkshopTestCase):
    """Test case for the views related to the ``WorkshopStepQuestion`` model"""

    @classmethod
    def setUpTestData(cls):
        """Build the data expected by the ``WorkshopStepQuestionAnswerFormSet``"""

        super().setUpTestData()

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

        cls.create_url = WorkshopStepQuestion.get_create_url(workshop=cls.workshop)

        # Data to create a new question
        cls.formset_create_data = {
            'title': 'New question',
            'is_multiple_choices': True,
            'is_all_or_nothing': True,
            # answers meta-data
            'answers-TOTAL_FORMS': 0,  # we have 0 choices
            'answers-INITIAL_FORMS': 0,
            'answers-MIN_NUM_FORMS': 0,
            'answers-MAX_NUM_FORMS': 5,
        }

        # Data to update an existing question
        cls.formset_update_data = {
            # question
            'title': cls.workshop_question.title,
            'is_multiple_choices': cls.workshop_question.is_multiple_choices,
            'is_all_or_nothing': cls.workshop_question.is_all_or_nothing,
            # answers meta-data
            'answers-TOTAL_FORMS': cls.workshop_question.answers.count(),
            'answers-INITIAL_FORMS': cls.workshop_question.answers.count(),
            'answers-MIN_NUM_FORMS': 0,
            'answers-MAX_NUM_FORMS': 5,
        }

        # answers data
        for index, answer in enumerate(cls.workshop_question.answers.all()):
            cls.formset_update_data.update({
                'answers-%s-id' % index: answer.pk,
                'answers-%s-order' % index: index + 1,
                'answers-%s-label' % index: answer.label,
                'answers-%s-is_correct' % index: answer.is_correct,
                'answers-%s-points' % index: answer.points,
                'answers-%s-question' % index: cls.workshop_question.pk,
            })

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

        self.login_as_trainer()

        response = self.client.post(self.create_url, self.formset_create_data)
        self.assertTrue(response.status_code, 200)
        self.assertTrue(WorkshopStepQuestion.objects.filter(title='New question').exists())

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

        self.login_as_trainer()

        self.formset_create_data['answers-TOTAL_FORMS'] = 1

        # Data for the answer
        self.formset_create_data.update({
            'answers-0-order': 0,
            'answers-0-label': 'test label',
            'answers-0-is_correct': True,
            'answers-0-points': 1,
        })

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

        # Our question was created
        question = WorkshopStepQuestion.objects.get(title='New question')

        # this question got one answer, the one we created
        self.assertEqual(question.answers.count(), 1)
        answer = question.answers.first()
        self.assertEqual(answer.label, 'test label')
        self.assertEqual(answer.is_correct, True)
        self.assertEqual(answer.points, 1)

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

        self.login_as_trainer()

        # We update the title of the question
        self.formset_update_data['title'] = 'Updated title'
        # And the label, points of the first answer
        self.formset_update_data['answers-0-label'] = 'Updated label'
        self.formset_update_data['answers-0-points'] = 10

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

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

        self.workshop_question.refresh_from_db()
        self.assertEqual(self.workshop_question.title, 'Updated title')

        first_answer = self.workshop_question.answers.first()
        self.assertEqual(first_answer.label, 'Updated label')
        self.assertEqual(first_answer.points, 10)

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

        self.login_as_trainer()

        # At first, we got two answers on the question
        self.assertEqual(self.workshop_question.answers.count(), 2)

        formset_update_data = self.formset_update_data.copy()

        formset_update_data['answers-TOTAL_FORMS'] = 3
        formset_update_data.update({
            'answers-2-order': 0,
            'answers-2-label': 'test label',
            'answers-2-is_correct': True,
            'answers-2-points': 1,
            'answers-2-question': self.workshop_question.pk,
        })

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

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

        # We should now have a third answer, the one we just created
        self.assertEqual(self.workshop_question.answers.count(), 3)

    def test_update_view_should_allow_soft_delete_answer(self):
        """Test that the update view should allow us to soft-delete an answer"""

        self.login_as_trainer()

        # We got two non-deleted answers
        self.assertEqual(self.workshop_question.answers.non_deleted().count(), 2)

        # Add post data to set the first answer as deleted
        self.formset_update_data['answers-0-DELETE'] = True

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

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

        # We got only one non-deleted answer
        self.assertEqual(self.workshop_question.answers.non_deleted().count(), 1)

        # But still a total of two answers
        self.assertEqual(self.workshop_question.answers.count(), 2)

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

        # At first, we got 4 non-deleted steps, and 4 steps in total
        self.assertEqual(self.workshop.steps.non_deleted().count(), 4)
        self.assertEqual(self.workshop.steps.count(), 4)

        self.login_as_trainer()

        # We delete the first step
        response = self.client.post(
            self.workshop.get_steps_delete_url(),
            {
                'steps': [self.workshop.steps.first().id]
            }
        )

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

        # We should have 3 steps non-deleted and 4 steps in total
        self.assertEqual(self.workshop.steps.non_deleted().count(), 3)
        self.assertEqual(self.workshop.steps.count(), 4)

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

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

        # We should have 1 step non-deleted and 4 steps in total
        self.assertEqual(self.workshop.steps.non_deleted().count(), 1)
        self.assertEqual(self.workshop.steps.count(), 4)


class WorkshopDeleteViewTestCase(WorkshopTestCase):
    """Test case that tests the ``WorkshopDeleteView`` view."""

    @classmethod
    def setUpTestData(cls):
        super().setUpTestData()

        cls.workshop_delete_url = cls.workshop.get_delete_url()
        cls.training.version_state = cls.training.VERSION_STATES.DRAFT
        cls.training.save()

    def test_user_not_owner_should_be_rejected(self):
        """Test that a user not owner should't be allowed to soft delete quiz."""

        # this trainer is not owner of ``self.quiz``
        self.login_as_admin()

        response = self.client.post(self.workshop_delete_url, {}, follow=True)
        self.assertEqual(response.status_code, 404)

    def test_owner_should_be_accepted(self):
        """Test that user of quiz should be allowed to soft delete quiz.

        Notes
        -----
        The training of the workshop should be in a ``PUBLISHED`` state.

        """

        self.login_as_trainer()

        response = self.client.post(self.workshop_delete_url, {}, follow=True)
        self.assertEqual(response.status_code, 200)

    def test_quiz_is_soft_deleted(self):
        """Test that the quiz is soft deleted.

        Notes
        -----
        The training of the workshop should be in a ``PUBLISHED`` state.

        """

        self.login_as_trainer()

        self.client.post(self.workshop_delete_url, {})
        self.workshop.refresh_from_db()
        self.assertTrue(self.workshop.is_deleted)


class XLSQuizImportViewTestCase(WorkshopTestCase):
    """Test the ``XLSQuizImportView`` view."""

    def test_template_example_file(self):
        """Test the ``template_example_file`` variable exists."""

        self.login_as_trainer()
        response = self.client.get(reverse('workshops:import-xls', kwargs={
            'training_uuid': self.training.id,
        }))

        self.assertIn('template_example_file', response.context)
        self.assertIsNotNone(response.context['template_example_file'])


class XLSQuizExportViewTestCase(WorkshopTestCase):
    """Test the ``XLSQuizExportView`` view."""

    def test_the_view_should_raise_a_404_if_the_workshop_is_not_exportable(self):
        """Test that the view should raise a 404 if the workshop is not exportable"""

        self.login_as_trainer()

        # We remove the "image" step, so we can fetch the export url
        self.workshop_question_mc_image.delete()

        export_url = self.workshop.get_export_url()

        # The route is working
        response = self.client.get(export_url)
        self.assertEqual(response.status_code, 200)

        # We add a new "image" step
        WorkshopStepImageFactory(
            workshop=self.workshop
        )

        # The route should raise a 404, because we do have an "image" step
        response = self.client.get(export_url)
        self.assertEqual(response.status_code, 404)
