"""This module holds activities tests for ``assessments`` app."""

from django.contrib.contenttypes.models import ContentType
from django.core.urlresolvers import reverse
from django.db import transaction, IntegrityError

from mtp.apps.assessments.factories.activities import AssessableActivityPartUserScoreFactory
from mtp.apps.roleplays.factories import (
    AssessingRoleplayFactory,
    RoleplayFactory,
    StepFactory,
)
from mtp.apps.trainings.factories import TrainingFactory
from mtp.apps.workshops.factories import (
    AssessingWorkshopFactory,
    WorkshopFactory,
)
from mtp.apps.workshops.models import WorkshopStepQuestion

from .test_cases import MTPComplexAssessmentTestCase, MTPSimpleAssessmentTestCase


class AssessableFormsTestCase(MTPSimpleAssessmentTestCase):
    """Test case that tests the cms forms for assessable objects."""

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

        cls.assessing_training = cls.training
        cls.non_assessing_training = TrainingFactory(
            owner=cls.trainer,
            job_profile=None
        )

        # We need assessing and non-assessing activities.
        cls.assessing_workshop = AssessingWorkshopFactory(
            training=cls.training
        )
        cls.assessing_roleplay = AssessingRoleplayFactory(
            training=cls.training
        )
        cls.assessing_roleplay_step = StepFactory(
            roleplay=cls.assessing_roleplay
        )

        cls.non_assessing_workshop = WorkshopFactory(
            training=cls.non_assessing_training
        )
        cls.non_assessing_roleplay = RoleplayFactory(
            training=cls.non_assessing_training
        )
        cls.non_assessing_roleplay_step = StepFactory(
            roleplay=cls.non_assessing_roleplay
        )

    def setUp(self):
        super().setUp()

        self.login_as_trainer()

    def _assert_assessing_box_exists(self, html):
        """Helper: Ensure the ``is_assessing`` checkbox is present in the html."""

        checkbox = html.find('input', attrs={
            'type': 'checkbox',
            'name': 'is_assessing',
        })
        self.assertIsNotNone(checkbox)
        return checkbox

    def _assert_assessing_box_is_checked(self, checkbox):
        """Helper: Ensure the ``is_assessing`` checkbox is checked."""

        self.assertIn('checked', checkbox.attrs)

    def _assert_assessing_box_is_not_checked(self, checkbox):
        """Helper: Ensure the ``is_assessing`` checkbox is not checked."""

        self.assertNotIn('checked', checkbox.attrs)

    def _assert_response_contains_checked_assessing_box(self, url):
        """Helper: Ensure the response contains a checked ``is_assessing``
        checkbox."""

        html = self._parse_url(url)
        checkbox = self._assert_assessing_box_exists(html)
        self._assert_assessing_box_is_checked(checkbox)

    def test_assessable_activity_in_assessable_training_defaults_to_assessing(self):
        """Test that the cms checks the ``is_assessing`` checkbox when creating
        an assessing activity in the context of an assessing training."""

        create_roleplay_url = reverse('trainings:roleplay-create', kwargs={
            'training_uuid': self.assessing_training.id,
        })
        self._assert_response_contains_checked_assessing_box(create_roleplay_url)

        create_workshop_url = reverse('trainings:workshop-create', kwargs={
            'training_uuid': self.assessing_training.id,
        })
        self._assert_response_contains_checked_assessing_box(create_workshop_url)

    def test_activity_in_non_assessable_training_is_not_assessable(self):
        """Test that the cms does not display the ``is_assessing`` checkbox when
        creating an assessing activity in the context of a non-assessing
        training."""

        create_roleplay_url = reverse('trainings:roleplay-create', kwargs={
            'training_uuid': self.non_assessing_training.id,
        })
        create_workshop_url = reverse('trainings:workshop-create', kwargs={
            'training_uuid': self.non_assessing_training.id,
        })

        # a training without job_profile prohibit its activities to be assessing
        self.assertIsNone(self.non_assessing_training.job_profile)

        response = self.client.get(create_roleplay_url, follow=True)

        self.assertEqual(response.status_code, 200)
        self.assertNotIn('is_assessing', response.context['form'].fields)

        response = self.client.get(create_workshop_url, follow=True)

        self.assertEqual(response.status_code, 200)
        self.assertNotIn('is_assessing', response.context['form'].fields)


class AssessableActivityPartUserScoreModelTestCase(MTPComplexAssessmentTestCase):
    """Test case for the ``AssessableActivityPartUserScore`` model."""

    @classmethod
    def setUpTestData(cls):
        """Prepare the content_type of ``WorkshopStepQuestion`` model."""

        super().setUpTestData()

        cls.content_type = ContentType.objects.get_for_model(WorkshopStepQuestion)

    def test_uniqueness(self):
        """Check we can't have 2 scores for the same user, session and activity_part."""

        AssessableActivityPartUserScoreFactory(
            user=self.trainee,
            session=self.training_session,
            activity_part_id=self.quiz_questions[0].id,
            activity_part_type=self.content_type,
        )

        with transaction.atomic():
            with self.assertRaises(IntegrityError):
                AssessableActivityPartUserScoreFactory(
                    user=self.trainee,
                    session=self.training_session,
                    activity_part_id=self.quiz_questions[0].id,
                    activity_part_type=self.content_type,
                )
