"""Test the management commands of the ``trainings`` app"""

from io import StringIO
from mock import patch

from django.core.management import call_command, CommandError
from django.test import override_settings

from mtp.apps.common.tests.test_cases import MTPTestCase
from mtp.apps.trainings.management.commands.close_outdated_sessions import Command

from ..models import TrainingSession
from ..redis_models import TrainingSession as RedisTrainingSession


class UpdateSessionsStatsTestcase(MTPTestCase):
    """Test cases for the ``update_sessions_stats`` command."""

    def setUp(self):
        """Clear redis data before each test."""

        super().setUp()

        # Clear all sessions data from the redis model
        for entry in RedisTrainingSession.collection().instances():
            entry.delete()

    def test_command_without_session_data_should_work(self):
        """Test that the command without session data will not update anything."""

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

        # Check its output
        output = out.getvalue()
        self.assertIn('0 session(s) to update', output)
        self.assertIn('0 successfully updated session(s)', output)

        # The session is not updated
        # (we need to refetch it from the db to be sure)
        self.training_session.refresh_from_db()
        self.assertIsNone(self.training_session.average_tablet_usage_time)


@override_settings(INSTANCE_IS_MASTER=True)
class CloseOutdatedSessionsTestcase(MTPTestCase):
    """Test cases for the ``close_outdated_sessions`` command"""

    def test_command_should_work_interactive_and_yes_confirmation(self):
        """Test that the command should work with the ``interactive`` option"""

        # We got no closed session
        self.assertEqual(
            TrainingSession.objects.closed().count(),
            0
        )

        # Call the command
        out = StringIO()

        # We need to mock the ``get_input`` method to return "yes",
        # so the command don't wait for the user input
        with patch.object(Command, 'get_input', return_value='yes'):
            call_command('close_outdated_sessions', stdout=out)

        # Check its output
        output = out.getvalue()
        self.assertIn('1 session(s) successfully closed', output)

        # We got one closed session
        self.assertEqual(
            TrainingSession.objects.closed().count(),
            1
        )

    def test_command_should_work_interactive_and_no_confirmation(self):
        """Test that the command should work with the ``interactive`` option"""

        # We got no closed session
        self.assertEqual(
            TrainingSession.objects.closed().count(),
            0
        )

        # We need to mock the ``get_input`` method to return "no",
        # so the command don't wait for the user input
        with patch.object(Command, 'get_input', return_value='no'):
            with self.assertRaises(CommandError):
                call_command('close_outdated_sessions')

        # No session were closed
        self.assertEqual(
            TrainingSession.objects.closed().count(),
            0
        )

    def test_command_should_work_non_interactive(self):
        """Test that the command should work without the ``interactive`` option"""

        # We got no closed session
        self.assertEqual(
            TrainingSession.objects.closed().count(),
            0
        )

        # Call the command
        out = StringIO()
        call_command('close_outdated_sessions', '--noinput', stdout=out)

        # Check its output
        output = out.getvalue()
        self.assertIn('1 session(s) successfully closed', output)

        # We got one closed session
        self.assertEqual(
            TrainingSession.objects.closed().count(),
            1
        )

    def test_command_should_work_with_delta_after(self):
        """Test that the command should work without the ``delta_after`` option"""

        with patch.object(TrainingSession.objects, 'close_outdated') as patched:
            call_command('close_outdated_sessions',
                         '--noinput',
                         days_after=1)

        # The ``close_outdated_sessions`` was called with 1 day as the ``delta_after``
        patched.assert_called_once_with(days_after=1)
