"""This modules holds competencies tests for the ``assessments`` app."""

from django.contrib.contenttypes.models import ContentType

from django_lockable.models import Lockable
from django_lockable.utils import _no_fields_locking

from mtp.apps.accounts.factories import GroupFactory
from mtp.apps.common.tests.test_cases import MTPTestCase
from mtp.apps.workshops.models import Workshop

from ..factories import (
    CompetencyFactory,
    CompetencyTreeFactory,
    DimensionFactory,
)

from .test_cases import MTPComplexAssessmentTestCase, MTPSimpleAssessmentTestCase


class CardinalityTestCase(MTPTestCase):

    def test_objects_cardinality(self):
        """Test that competencies / dimensions can be created with the same
        name and code across different competency trees / competencies."""

        trees = CompetencyTreeFactory.create_batch(2)

        competencies = [
            CompetencyFactory(name='Test Competency', code='TEST', tree=tree)
            for tree in trees
        ]

        for competency in competencies:
            DimensionFactory(name='Test Dimension', code='TEST', competency=competency)


class VersionableCompetencyTreeTestCase(MTPSimpleAssessmentTestCase):
    """Tests the ``Versionable behaviour of the ``CompetencyTree`` model"""

    @classmethod
    def setUpTestData(cls):
        # Necessary data
        super().setUpTestData()

        # We will need one group
        cls.group = GroupFactory()

    def test_duplicate_to_groupsx(self):
        """Test the ``duplicate_to_groups`` method"""

        # Check the potentials duplicated competency trees
        potential_trees = self.competency_tree.duplicate_to_groups([self.group], dry_run=True)

        self.assertEqual(
            potential_trees,
            [self.group]
        )

        # Check the actually duplicated competency tree

        # Disable the lockable globally
        with _no_fields_locking(Lockable):
            duplicated_trees = self.competency_tree.duplicate_to_groups([self.group])

        self.assertEqual(
            duplicated_trees[0].job_profile,
            None
        )

        self.assertEqual(
            duplicated_trees[0].original_version,
            self.competency_tree
        )

        self.assertEqual(
            duplicated_trees[0].group,
            self.group
        )

        self.assertNotEqual(
            duplicated_trees[0].competencies.first(),
            self.competency_tree.competencies.first()
        )

        # Try to duplicate again, to the same group,
        # this should be not possible
        potential_trees = self.competency_tree.duplicate_to_groups([self.group], dry_run=True)

        self.assertEqual(
            potential_trees,
            []
        )

    def test_get_group_version(self):
        """Test the ``get_group_version`` method on the ``CompetencyTree`` model"""

        # Make a duplicate
        duplicated_tree = self.competency_tree.duplicate_to_groups([self.group])[0]

        # Try the ``get_group_version`` method on the parent
        self.assertEqual(
            duplicated_tree,
            self.competency_tree.get_group_version(self.group)
        )

        # Try the ``get_group_version`` method on the duplicated version
        self.assertIsNone(
            duplicated_tree.get_group_version(self.group)
        )

    def test_competency_group_version(self):
        """Test the ``group_version`` method on the ``Competency`` model"""

        # Before the duplicate, the ``group_version`` should return ``self``
        competency_group_version = self.competency_tree.competencies.first(
        ).group_version(self.group)

        self.assertEqual(
            competency_group_version,
            self.competencies[0]
        )

        # Make a duplicate
        duplicated_tree = self.competency_tree.duplicate_to_groups([self.group])[0]

        # Change the name of the first competency on the duplicate
        duplicate_first_competency = duplicated_tree.competencies.first()
        duplicate_first_competency.name = 'New name'
        duplicate_first_competency.save()

        competency_group_version = self.competency_tree.competencies.first(
        ).group_version(self.group)

        # The ``group_version`` should return the newly created version of the competency
        self.assertEqual(
            competency_group_version,
            duplicate_first_competency
        )

        # The name of the duplicate is the correct one
        self.assertEqual(
            competency_group_version.name,
            'New name'
        )

    def test_dimension_group_version(self):
        """Test the ``group_version`` method on the ``Dimension`` model"""

        # Before the duplicate, the ``group_version`` should return ``self``
        dimension_group_version = self.competency_tree.competencies.first(
        ).dimensions.first().group_version(self.group)

        self.assertEqual(
            dimension_group_version,
            self.dimensions[0]
        )

        # Make a duplicate
        duplicated_tree = self.competency_tree.duplicate_to_groups([self.group])[0]

        # Change the name of the first dimension on the duplicate
        duplicate_first_dimension = duplicated_tree.competencies.first().dimensions.first()
        duplicate_first_dimension.name = 'New name'
        duplicate_first_dimension.save()

        dimension_group_version = self.competency_tree.competencies.first(
        ).dimensions.first().group_version(self.group)

        # The ``group_version`` should return the newly created version of the dimension
        self.assertEqual(
            duplicate_first_dimension,
            dimension_group_version
        )

        # The name of the duplicate is the correct one
        self.assertEqual(
            dimension_group_version.name,
            'New name'
        )


class ActivityListCompetencyTreeTestCase(MTPSimpleAssessmentTestCase):
    """Test the ``_get_activities_list`` method of the ``CompetencyTree``"""

    @classmethod
    def setUpTestData(cls):
        """Set up the test data"""

        super().setUpTestData()

        cls.competency_tree._session = cls.training_session

    def get_expected_result(self):
        """Get the expected result"""

        expected_result = []
        for activity in self.training.activities.non_deleted().filter(is_assessing=True):
            content_type = ContentType.objects.get_for_model(activity.__class__)
            expected_result.append(
                {
                    'id': activity.id,
                    'type_id': content_type.id,
                    'name': activity.name,
                    'order': activity.sort_order,
                    'weight': activity.weight,
                    'max_points_yield': activity.max_points_yield
                }
            )

        return expected_result

    def test_get_activities_list(self):
        """Test the result of the ``_get_activities_list`` method"""

        # The output is correct
        self.assertCountEqual(
            self.competency_tree._get_activities_list(),
            self.get_expected_result()
        )

        # We soft-delete an activity
        with _no_fields_locking(Lockable):
            self.training.activities.instance_of(Workshop).order_by('id').first().remove()

        # The output is still correct
        self.assertCountEqual(
            self.competency_tree._get_activities_list(),
            self.get_expected_result()
        )


class PrepareDebriefActivitiesCompetencyTreeTestCase(MTPComplexAssessmentTestCase):
    """Test case for the ``_prepare_debrief_activities`` method of the ``CompetencyTree``  model"""

    @classmethod
    def setUpTestData(cls):
        """Set up the test data"""

        super().setUpTestData()

        cls.competency_tree._session = cls.training_session
        cls.competency_tree._trainee = cls.trainee

    def test_soft_deleted_activities_should_not_be_included_in_the_output(self):
        """Test that the "soft-deleted" activities should not be included in the output"""

        self.assertEqual(
            len(self.competency_tree._prepare_debrief_activities()),
            1
        )

        self.roleplay.remove()

        self.assertEqual(
            len(self.competency_tree._prepare_debrief_activities()),
            0
        )
