"""Test the management commands of the ``surveys`` app."""

from io import StringIO

from django.core.management import call_command
from django.test import override_settings
from django.utils import timezone

from mtp.apps.surveys.models import Survey

from .test_cases import SurveyTestCase


@override_settings(SURVEYS_AUTO_COMPUTE_STATS=False)
class UpdateSurveysStatsTestcase(SurveyTestCase):
    """Test the ``update_surveys_stats`` management command."""

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

        cls.question1 = cls.single_choice_question
        cls.choices1 = cls.single_choice_choices
        cls.question2, cls.choices2 = cls.create_single_choice_question(cls.trainer_pool)

        # Update choices to have 1, 2, 3, 4, 5
        cls.set_choices_values(cls.choices1, 'value_number', list(range(1, 1 + len(cls.choices1))))
        cls.set_choices_values(cls.choices2, 'value_number', list(range(1, 1 + len(cls.choices2))))

        # Create a survey
        cls.survey, __ = Survey.objects.get_or_create_for_training(cls.training, cls.trainer)

    def test_command_without_param_should_use_new_answers(self):
        """Test that the command without parameter updates stats with only new answers."""

        # Add one answer to each question
        choice1 = self.get_choice_having_value(self.choices1, 'value_number', 1)
        self.save_answer(self.question1, [choice1])
        choice2 = self.get_choice_having_value(self.choices2, 'value_number', 3)
        self.save_answer(self.question2, [choice2])

        # Call the command
        out = StringIO()
        call_command('update_surveys_stats', stdout=out)

        # Check its output
        output = out.getvalue()
        self.assertIn('Successfully updated statistics', output)
        self.assertNotIn('Successfully updated statistics (forced)', output)
        self.assertIn('Updated questions: 2', output)
        self.assertIn('Used answers: 2', output)
        self.assertIn('Failed answers: 0', output)

        # Check stats
        day = str(timezone.now().date())
        self.check_sortedset(self.question1.redis_instance.nb_by_day, [(day, 1)])
        self.check_sortedset(self.question1.redis_instance.avg_by_day, [(day, 1)])
        self.check_sortedset(self.question2.redis_instance.nb_by_day, [(day, 1)])
        self.check_sortedset(self.question2.redis_instance.avg_by_day, [(day, 3)])

        # Calling it again should do nothing
        out = StringIO()
        call_command('update_surveys_stats', stdout=out)

        # Check its output
        output = out.getvalue()
        self.assertIn('Updated questions: 0', output)

        # Add a new answer
        choice3 = self.get_choice_having_value(self.choices2, 'value_number', 1)
        self.save_answer(self.question2, [choice3], trainee=self.trainees[1])

        # Calling it again should add the new answer
        out = StringIO()
        call_command('update_surveys_stats', stdout=out)

        # Check its output
        output = out.getvalue()
        self.assertIn('Updated questions: 1', output)
        self.assertIn('Used answers: 1', output)
        self.assertIn('Failed answers: 0', output)

        # Check stats
        self.check_sortedset(self.question2.redis_instance.nb_by_day, [(day, 2)])
        self.check_sortedset(self.question2.redis_instance.avg_by_day, [(day, 2)])

    def test_command_with_force_param_should_use_all_answers(self):
        """Test that the command with ``force=true`` updates stats with all answers."""

        # Add one answer to each question, precompute stats
        with override_settings(SURVEYS_AUTO_COMPUTE_STATS=True):
            choice1 = self.get_choice_having_value(self.choices1, 'value_number', 1)
            answer1 = self.save_answer(self.question1, [choice1])
            choice2 = self.get_choice_having_value(self.choices2, 'value_number', 3)
            self.save_answer(self.question2, [choice2])

        # Calling the command should do nothing (stats precomputed at create time)
        out = StringIO()
        call_command('update_surveys_stats', stdout=out)

        # Check its output
        output = out.getvalue()
        self.assertIn('Updated questions: 0', output)

        # Check stats
        day = str(timezone.now().date())
        self.check_sortedset(self.question1.redis_instance.nb_by_day, [(day, 1)])
        self.check_sortedset(self.question1.redis_instance.avg_by_day, [(day, 1)])
        self.check_sortedset(self.question2.redis_instance.nb_by_day, [(day, 1)])
        self.check_sortedset(self.question2.redis_instance.avg_by_day, [(day, 3)])

        # Update an answer to verify that stats will be updated
        choice3 = self.get_choice_having_value(self.choices2, 'value_number', 2)
        answer1.choices = [choice3]

        # Calling it with ``force=True`` should use all answers
        out = StringIO()
        call_command('update_surveys_stats', force=True, stdout=out)

        # Check its output
        output = out.getvalue()
        self.assertIn('Successfully updated statistics (forced)', output)
        self.assertIn('Updated questions: 2', output)
        self.assertIn('Used answers: 2', output)
        self.assertIn('Failed answers: 0', output)

        # Check stats
        day = str(timezone.now().date())
        self.check_sortedset(self.question1.redis_instance.nb_by_day, [(day, 1)])
        self.check_sortedset(self.question1.redis_instance.avg_by_day, [(day, 2)])
        self.check_sortedset(self.question2.redis_instance.nb_by_day, [(day, 1)])
        self.check_sortedset(self.question2.redis_instance.avg_by_day, [(day, 3)])
