"""This module contains the managers-related test cases of the ``ideas`` app"""

from django.test.utils import override_settings

from mtp.apps.brainstormers.constants import BRAINSTORMER_TYPE_CHOICES
from mtp.apps.brainstormers.factories import (
    BrainstormerFactory,
    BrainstormerPropositionFactory,
    BrainstormerPropositionGroupFactory,
    BrainstormerTemplateFactory,
)
from mtp.apps.brainstormers.models import (
    Brainstormer,
    BrainstormerProposition,
    BrainstormerPropositionGroup,
    BrainstormerTemplate,
)
from mtp.apps.common.tests.test_cases import MTPTestCase
from mtp.apps.likes.models import Like
from mtp.apps.trainings.factories import TrainingSessionFactory

from ..factories import (
    BrainstormingFactory,
    BrainstormingIdeaFactory,
    BrainstormingTagFactory,
    QuestionWallIdeaFactory,
    WordCloudIdeaFactory
)
from ..models import (
    Brainstorming,
    BrainstormingIdea,
    BrainstormingTag,
    QuestionWall,
    QuestionWallIdea,
    WordCloud,
    WordCloudIdea
)


from .test_cases import IdeasTestCase


class IdeasActivityManagerTestCase(MTPTestCase):
    """Test case for the ``IdeasManager`` Manager"""

    @classmethod
    def setUpTestData(cls):
        """Create some brainstormers data to migrate"""

        super().setUpTestData()

        # Brainstormer templates
        cls.template_propositionwall = BrainstormerTemplateFactory(
            brainstormer_type=BRAINSTORMER_TYPE_CHOICES.PROPOSITIONWALL,
            training=cls.training
        )
        cls.template_questionwall = BrainstormerTemplateFactory(
            brainstormer_type=BRAINSTORMER_TYPE_CHOICES.QUESTIONWALL,
            training=cls.training
        )
        cls.template_wordcloud = BrainstormerTemplateFactory(
            brainstormer_type=BRAINSTORMER_TYPE_CHOICES.WORDCLOUD,
            training=cls.training
        )

        # Brainstormers
        # -------------

        # One "propositionwall" (with groups and likes)
        cls.brainstormer_propositionwall = BrainstormerFactory(
            brainstormer_type=BRAINSTORMER_TYPE_CHOICES.PROPOSITIONWALL,
            session=cls.training_session
        )
        cls.propositionwall_group_1 = BrainstormerPropositionGroupFactory(
            brainstormer=cls.brainstormer_propositionwall
        )
        cls.propositionwall_group_2 = BrainstormerPropositionGroupFactory(
            brainstormer=cls.brainstormer_propositionwall
        )
        cls.propositionwall_proposition_1 = BrainstormerPropositionFactory(
            brainstormer=cls.brainstormer_propositionwall
        )
        cls.propositionwall_proposition_2 = BrainstormerPropositionFactory(
            brainstormer=cls.brainstormer_propositionwall
        )
        cls.propositionwall_proposition_1.groups.add(cls.propositionwall_group_1)
        cls.propositionwall_proposition_1.groups.add(cls.propositionwall_group_2)

        cls.propositionwall_proposition_2.groups.add(cls.propositionwall_group_2)

        cls.trainee.like(cls.propositionwall_proposition_1)
        cls.trainee.like(cls.propositionwall_proposition_2)

        # One "questionwall"
        cls.brainstormer_questionwall = BrainstormerFactory(
            brainstormer_type=BRAINSTORMER_TYPE_CHOICES.QUESTIONWALL,
            session=cls.training_session
        )
        cls.questionwall_proposition = BrainstormerPropositionFactory(
            brainstormer=cls.brainstormer_propositionwall
        )

        # One "wordcloud"
        cls.brainstormer_wordcloud = BrainstormerFactory(
            brainstormer_type=BRAINSTORMER_TYPE_CHOICES.WORDCLOUD,
            session=cls.training_session
        )
        cls.wordcloud_proposition = BrainstormerPropositionFactory(
            brainstormer=cls.brainstormer_propositionwall
        )

    def test_migrate_brainstormer_groups_should_migrate_the_brainstormer_groups(self):
        """Test that the ``migrate_brainstormer_groups`` method should work correctly"""

        # At first, we should have two ``BrainstormerPropositionGroup``s
        self.assertEqual(BrainstormerPropositionGroup.objects.count(), 2)

        # Create our new ``Brainstorming`` for which we want to migrate the groups
        brainstorming = BrainstormingFactory(session=self.training_session, training=None)

        group_values_list = BrainstormerPropositionGroup.objects.values(
            'id',
            'name',
            'color',
            'is_deleted'
        )

        Brainstorming.objects.migrate_brainstormer_groups(
            brainstorming,
            group_values_list
        )

        # Our ``Brainstorming`` should have two new ``BrainstormingTag``
        self.assertEqual(brainstorming.tags.count(), 2)

        # Our newly ``BrainstormingTag`` should have correct attributes
        proposition_group = BrainstormerPropositionGroup.objects.first()
        tag = BrainstormingTag.objects.get(id=proposition_group.id)

        self.assertEqual(tag.name, proposition_group.name)
        self.assertEqual(tag.color, proposition_group.color)
        self.assertEqual(tag.session, proposition_group.brainstormer.session)

    def test_migrate_brainstormer_propositions_should_migrate_the_brainstormer_propositions(self):
        """Test that the ``migrate_brainstormer_propositions`` should work correctly"""

        # We should have 4 propositions to migrate
        self.assertEqual(BrainstormerProposition.objects.count(), 4)

        # Create our new ``Brainstorming`` for which we want to migrate the propositions
        brainstorming = BrainstormingFactory(session=self.training_session, training=None)

        # We want to migrate the groups first
        group_values_list = BrainstormerPropositionGroup.objects.values(
            'id',
            'name',
            'color',
            'is_deleted'
        )

        Brainstorming.objects.migrate_brainstormer_groups(
            brainstorming,
            group_values_list
        )

        # Now, we can migrate the propositions
        propositions_values = BrainstormerProposition.objects.values(
            'id',
            'text',
            'user_id',
        )

        for proposition in propositions_values:
            proposition['groups'] = list(
                BrainstormerPropositionGroup.objects.filter(
                    propositions__in=[proposition['id']]
                ).values_list('id', flat=True)
            )

            propositions_likes = Like.objects.filter(
                object_uuid=proposition['id']
            ).values(
                'id',
                'author_id',
                'weight',
                'date_time',
                'is_deleted',
            )

            proposition['likes'] = list(propositions_likes)

            # Need to delete the likes to avoid the id clash
            Like.objects.filter(object_uuid=proposition['id']).delete()

        Brainstorming.objects.migrate_brainstormer_propositions(
            brainstorming,
            propositions_values
        )

        # Our ``Brainstorming`` should have 4 new ideas
        self.assertEqual(brainstorming.ideas.count(), 4)

        # The ideas should have correct attributes (we only check the first one)
        first_proposition = BrainstormerProposition.objects.first()
        new_idea = BrainstormingIdea.objects.get(id=first_proposition.id)

        self.assertEqual(new_idea.text, first_proposition.text)
        self.assertEqual(new_idea.user, first_proposition.user)

        # We should also have two tags...
        self.assertEqual(brainstorming.tags.count(), 2)

        # The second tag should have two ideas associated with it
        self.assertEqual(
            brainstorming.tags.get(id=self.propositionwall_group_2.id).ideas.count(),
            2
        )

        # The first tag should have one idea associated with it
        self.assertEqual(
            brainstorming.tags.get(id=self.propositionwall_group_1.id).ideas.count(),
            1
        )

        # We should also have the likes...
        self.assertEqual(new_idea.likes.count(), 1)

        # With correct attributes
        like_object = new_idea.likes.first()
        self.assertEqual(like_object.author, self.trainee)

    def test_migrate_brainstormers_templates_should_migrate_the_brainstormers_templates(self):
        """Test ``migrate_brainstormer_templates`` should migrate the brainstormer templates"""

        # At first, we don't have any ``IdeasActivity``
        self.assertEqual(Brainstorming.objects.count(), 0)
        self.assertEqual(QuestionWall.objects.count(), 0)
        self.assertEqual(WordCloud.objects.count(), 0)

        # But we got three ``BrainstormerTemplate``s
        self.assertEqual(
            BrainstormerTemplate.objects.count(),
            3
        )

        # Migrate the templates
        Brainstorming.objects.migrate_brainstormer_templates(template_model=BrainstormerTemplate)

        # Our 3 templates were successfully migrated
        self.assertEqual(Brainstorming.objects.count(), 1)
        self.assertEqual(QuestionWall.objects.count(), 1)
        self.assertEqual(WordCloud.objects.count(), 1)

        # Our newly created ``Brainstorming`` got correct values
        brainstorming = Brainstorming.objects.get()
        self.assertEqual(brainstorming.id, self.template_propositionwall.id)
        self.assertEqual(brainstorming.name, self.template_propositionwall.name)
        self.assertEqual(brainstorming.description, self.template_propositionwall.description)
        self.assertEqual(brainstorming.sort_order, self.template_propositionwall.sort_order)
        self.assertEqual(brainstorming.training, self.template_propositionwall.training)

        # Our newly created ``QuestionWall`` got correct values
        questionwall = QuestionWall.objects.get()
        self.assertEqual(questionwall.id, self.template_questionwall.id)
        self.assertEqual(questionwall.name, self.template_questionwall.name)
        self.assertEqual(questionwall.description, self.template_questionwall.description)
        self.assertEqual(questionwall.sort_order, self.template_questionwall.sort_order)
        self.assertEqual(questionwall.training, self.template_questionwall.training)

        # Our newly created ``WordCloud`` got correct values
        wordcloud = WordCloud.objects.get()
        self.assertEqual(wordcloud.id, self.template_wordcloud.id)
        self.assertEqual(wordcloud.name, self.template_wordcloud.name)
        self.assertEqual(wordcloud.description, self.template_wordcloud.description)
        self.assertEqual(wordcloud.sort_order, self.template_wordcloud.sort_order)
        self.assertEqual(wordcloud.training, self.template_wordcloud.training)

        # We shouldn't have any of the ``BrainstormerTemplate`` instances left
        self.assertEqual(BrainstormerTemplate.objects.count(), 0)

    @override_settings(INSTANCE_IS_SLAVE=True)
    def test_migrate_template_on_slave_instance_should_not_set_update_locally_to_true(self):
        """Test that the template migration should not set the update_locally to ``True``"""

        Brainstorming.objects.migrate_brainstormer_templates(template_model=BrainstormerTemplate)
        brainstorming = Brainstorming.objects.get()

        self.assertFalse(brainstorming.update_locally)

    def test_migrate_brainstormers_should_migrate_the_brainstormers(self):
        """Test ``migrate_brainstormers`` should migrate the brainstormers"""

        # At first, we don't have any ``IdeasActivity``
        self.assertEqual(Brainstorming.objects.count(), 0)
        self.assertEqual(QuestionWall.objects.count(), 0)
        self.assertEqual(WordCloud.objects.count(), 0)

        # But we got three ``Brainstormers``s
        self.assertEqual(
            Brainstormer.objects.count(),
            3
        )

        # Migrate the brainstormers
        Brainstorming.objects.migrate_brainstormers()

        # Our 3 brainstormers were successfully migrated
        self.assertEqual(Brainstorming.objects.count(), 1)
        self.assertEqual(QuestionWall.objects.count(), 1)
        self.assertEqual(WordCloud.objects.count(), 1)

        # Our newly created ``Brainstorming`` got correct values
        brainstorming = Brainstorming.objects.get()
        self.assertEqual(brainstorming.id, self.brainstormer_propositionwall.id)
        self.assertEqual(brainstorming.name, self.brainstormer_propositionwall.name)
        self.assertEqual(brainstorming.description, self.brainstormer_propositionwall.description)
        self.assertEqual(brainstorming.is_anonymous, self.brainstormer_propositionwall.anonymous)
        self.assertEqual(brainstorming.sort_order, self.brainstormer_propositionwall.sort_order)
        self.assertEqual(brainstorming.session, self.brainstormer_propositionwall.session)

        # And a correct number of tags / ideas
        self.assertEqual(brainstorming.ideas.count(), 4)
        self.assertEqual(brainstorming.tags.count(), 2)

        # Our newly created ``QuestionWall`` got correct values
        questionwall = QuestionWall.objects.get()
        self.assertEqual(questionwall.id, self.brainstormer_questionwall.id)
        self.assertEqual(questionwall.name, self.brainstormer_questionwall.name)
        self.assertEqual(questionwall.description, self.brainstormer_questionwall.description)
        self.assertEqual(questionwall.is_anonymous, self.brainstormer_questionwall.anonymous)
        self.assertEqual(questionwall.sort_order, self.brainstormer_questionwall.sort_order)
        self.assertEqual(questionwall.session, self.brainstormer_questionwall.session)

        # Our newly created ``WordCloud`` got correct values
        wordcloud = WordCloud.objects.get()
        self.assertEqual(wordcloud.id, self.brainstormer_wordcloud.id)
        self.assertEqual(wordcloud.name, self.brainstormer_wordcloud.name)
        self.assertEqual(wordcloud.description, self.brainstormer_wordcloud.description)
        self.assertEqual(wordcloud.is_anonymous, self.brainstormer_wordcloud.anonymous)
        self.assertEqual(wordcloud.sort_order, self.brainstormer_wordcloud.sort_order)
        self.assertEqual(wordcloud.session, self.brainstormer_wordcloud.session)

        # We shouldn't have any of the ``Brainstormer`` instances left
        self.assertEqual(Brainstormer.objects.count(), 0)


class BrainstormingTagQuerySetTestCase(IdeasTestCase):
    """Test case for the ``BrainstormingTagQueryset`` related to the ``BrainstormingTag`` model"""

    def test_for_session_should_return_only_the_tags_linked_to_a_given_session(self):
        """Test that the ``for_session`` method should allow us to filter using the session"""

        # At first, we got no ideas linked to the current session...
        self.assertEqual(BrainstormingTag.objects.for_session(self.training_session).count(), 0)
        # ...but one tag from initial data, linked to the training
        self.assertEqual(BrainstormingTag.objects.count(), 1)

        # Create a new idea, we should now have one...
        BrainstormingTagFactory(brainstorming=self.brainstorming, session=self.training_session)
        self.assertEqual(BrainstormingTag.objects.for_session(self.training_session).count(), 1)

        # Create another idea in another session, we should still have one
        BrainstormingTagFactory(brainstorming=self.brainstorming, session=TrainingSessionFactory())
        self.assertEqual(BrainstormingTag.objects.for_session(self.training_session).count(), 1)


class BrainstormingIdeaQuerySetTestCase(IdeasTestCase):
    """Test case for ``BrainstormingIdeaQueryset`` related to the ``BrainstormingIdea`` model"""

    def test_for_session_should_return_only_the_ideas_linked_to_a_given_session(self):
        """Test that the ``for_session`` method should allow us to filter using the session"""

        # At first, we got no ideas linked to the current session...
        self.assertEqual(BrainstormingIdea.objects.for_session(self.training_session).count(), 0)
        # ...but one idea from initial data, linked to the training
        self.assertEqual(BrainstormingIdea.objects.count(), 1)

        # Create a new idea, we should now have one
        BrainstormingIdeaFactory(brainstorming=self.brainstorming, session=self.training_session)
        self.assertEqual(
            BrainstormingIdea.objects.for_session(self.training_session).count(),
            1
        )

        # Create another idea in another session, we should still have one
        BrainstormingIdeaFactory(
            brainstorming=self.brainstorming,
            session=TrainingSessionFactory()
        )
        self.assertEqual(
            BrainstormingIdea.objects.for_session(self.training_session).count(),
            1
        )


class QuestionWallIdeaQuerySetTestCase(IdeasTestCase):
    """Test case for the ``IdeaQueryset`` related to the ``QuestionWallIdea`` model"""

    def test_for_session_should_return_only_the_ideas_linked_to_a_given_session(self):
        """Test that the ``for_session`` method should allow us to filter using the session"""

        # At first, we got no ideas linked to the current session
        self.assertEqual(
            QuestionWallIdea.objects.for_session(self.training_session).count(),
            0
        )

        # Create a new idea, we should now have one
        QuestionWallIdeaFactory(questionwall=self.questionwall, session=self.training_session)
        self.assertEqual(
            QuestionWallIdea.objects.for_session(self.training_session).count(),
            1
        )

        # Create another idea in another session, we should still have one
        QuestionWallIdeaFactory(questionwall=self.questionwall, session=TrainingSessionFactory())
        self.assertEqual(
            QuestionWallIdea.objects.for_session(self.training_session).count(),
            1
        )


class WordCloudIdeaQuerySetTestCase(IdeasTestCase):
    """Test case for the ``IdeaQueryset`` related to the ``WordCloudIdea`` model"""

    def test_for_session_should_return_only_the_ideas_linked_to_a_given_session(self):
        """Test that the ``for_session`` method should allow us to filter using the session"""

        # At first, we got no ideas linked to the current session
        self.assertEqual(
            WordCloudIdea.objects.for_session(self.training_session).count(),
            0
        )

        # Create a new idea, we should now have one
        WordCloudIdeaFactory(wordcloud=self.wordcloud, session=self.training_session)
        self.assertEqual(
            WordCloudIdea.objects.for_session(self.training_session).count(),
            1
        )

        # Create another idea in another session, we should still have one
        WordCloudIdeaFactory(wordcloud=self.wordcloud, session=TrainingSessionFactory())
        self.assertEqual(
            WordCloudIdea.objects.for_session(self.training_session).count(),
            1
        )
