"""Tests related to the ``accounts`` app"""

import unittest

from django.conf import settings
from django.contrib.auth.decorators import user_passes_test
from django.contrib.auth.models import AnonymousUser
from django.contrib.messages import constants as MSG
from django.contrib.staticfiles import finders
from django.core import mail
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
from django.core.urlresolvers import reverse
from django.db.utils import IntegrityError
from django.http import HttpResponse
from django.test import RequestFactory, TestCase
from django.utils.decorators import method_decorator
from django.utils.translation import ugettext as _
from django.utils import timezone
from django.views import generic

from mtp.apps.assessments.models import CompetencyTree
from mtp.apps.common.tests.test_cases import (
    DEFAULT_PASSWORD, MTPTestCase,
    SECURE_PASSWORD
)
from mtp.apps.common.tests.waffle_test_utils import override_switch
from mtp.apps.common.utils import url_to_path

from .constants import (
    GROUPTYPE_JOB_PROFILE,
)
from .factories import (
    AdminFactory,
    GroupFactory,
    GroupRoleFactory,
    GroupTypeFactory,
    JobProfileFactory,
    TraineeFactory,
    TrainerFactory,
)
from .models import Group, GroupRole, MTPUser
from .utils import init_user, validate_extra_secure_password, xls_import_trainee


class UtilsTestCase(MTPTestCase):

    def test_validate_extra_secure_password(self):
        """
        Test the password validation function.
        """

        # This password is too short.
        password = 'aze'
        is_valid, __, code = validate_extra_secure_password(password)
        self.assertFalse(is_valid)
        self.assertEqual(code, 'invalid-length')

        # This password contains no uppercase letter.
        password = 'azertyui'
        is_valid, __, code = validate_extra_secure_password(password)
        self.assertFalse(is_valid)
        self.assertEqual(code, 'missing-characters')

        # This password contains no lowercase letter.
        password = 'AZERTYUI'
        is_valid, __, code = validate_extra_secure_password(password)
        self.assertFalse(is_valid)
        self.assertEqual(code, 'missing-characters')

        # This password contains no digit.
        password = 'AZERtyui'
        is_valid, __, code = validate_extra_secure_password(password)
        self.assertFalse(is_valid)
        self.assertEqual(code, 'missing-characters')

        # This password contains no special character.
        password = '4Z3Rtyui'
        is_valid, __, code = validate_extra_secure_password(password)
        self.assertFalse(is_valid)
        self.assertEqual(code, 'missing-characters')

        # This password is identical to the previous one.
        password = '4Z3Rtyu!'
        is_valid, __, code = validate_extra_secure_password(password, previous=password)
        self.assertFalse(is_valid)
        self.assertEqual(code, 'identical-previous')

        # This password looks good.
        password = '4Z3Rtyu!'
        is_valid, __, __ = validate_extra_secure_password(password)
        self.assertTrue(is_valid)

    def find_users_file_path(self):
        """Find the users import file"""

        # First, we find the sample import file
        users_file_path = finders.find('users_import.xls')

        # The sample file exists
        self.assertIsNotNone(users_file_path)

        return users_file_path

    def test_xls_import_trainee_commit_equal_false(self):
        """Test the users import from xls file with commit=False"""

        # We try to import the users from the file
        users = xls_import_trainee(self.find_users_file_path(), commit=False)

        # The users list should contain 1 user
        self.assertEqual(len(users), 1)

        user = users[0]

        self.assertEqual(user.email, 'email@magency.fr')
        self.assertEqual(user.username, 'u1')
        self.assertEqual(user.first_name, 'User')
        self.assertEqual(user.last_name, 'ONE')

        # We should dont get any users when fetching from the db
        users = MTPUser.objects.filter(username=user.username)

        self.assertEqual(len(users), 0)

    def test_xls_import_trainee_commit_equal_true(self):
        """Test the users import from xls file with commit=True"""

        # We try to import the users from the file
        users = xls_import_trainee(self.find_users_file_path(), commit=True)

        # The users list should contain 1 user
        self.assertEqual(len(users), 1)

        user = users[0]

        self.assertEqual(user.email, 'email@magency.fr')
        self.assertEqual(user.username, 'u1')
        self.assertEqual(user.first_name, 'User')
        self.assertEqual(user.last_name, 'ONE')

        # We should got 1 user when fetching from the db
        users = MTPUser.objects.filter(username=user.username)

        self.assertEqual(len(users), 1)


class JobProfileTestCase(MTPTestCase):
    """Test case to test behaviors related to job profile groups."""

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

        # We need the "job profile" group type...
        cls.group_type_job_profile = GroupTypeFactory(name=GROUPTYPE_JOB_PROFILE)

        # ... and a job profile.
        cls.job_profile = JobProfileFactory()

    def test_set_job_profile(self):
        self.trainee.job_profile = self.job_profile
        self.assertIn(self.job_profile, self.trainee.mgroups.all())

    def test_set_job_profile_raises_if_group_is_not_a_job_profile(self):
        not_a_job_profile = GroupFactory()
        with self.assertRaises(ValueError):
            self.trainee.job_profile = not_a_job_profile

    def test_get_users_job_profile(self):
        self.trainee.job_profile = self.job_profile
        self.assertEqual(self.trainee.job_profile, self.job_profile)

    def test_automatic_competency_tree_creation(self):
        TEST_JOB_PROFILE_NAMES = ['test1', 'test2']

        # If the assessments are disabled, no competency tree creation.
        with override_switch('enable_assessments', False):
            JobProfileFactory(name=TEST_JOB_PROFILE_NAMES[0])
            self.assertFalse(
                CompetencyTree.objects.filter(name=TEST_JOB_PROFILE_NAMES[0]).exists()
            )

        # Otherwise, there should be a new competency tree for the job profile.
        with override_switch('enable_assessments', True):
            JobProfileFactory(name=TEST_JOB_PROFILE_NAMES[1])
            self.assertTrue(
                CompetencyTree.objects.filter(name=TEST_JOB_PROFILE_NAMES[1]).exists()
            )


class MTPUserModelTestCase(MTPTestCase):
    """Test case to test the ``MTPUser`` model"""

    def test_init_user_should_correctly_set_the_password(self):
        """
        Test the ``mtp.apps.account.utils.init_user`` method.
        """

        # We create a user with an empty password
        user = MTPUser.objects.create(username='test')
        user, password = init_user(user, password=None, groupset=None, is_staff=False)

        # The user should have a fresh new password :)
        self.assertEqual(
            len(password),
            8
        )

        # We should test the login with this new password.
        self.assertTrue(
            self.client.login(username=user.username, password=password)
        )

    def test_check_password_expiry(self):
        """
        Test the password expiry checking function.
        """

        # So, we have a trainer who hasn't changed their password in a while...
        now = timezone.now()
        two_months_ago = now - timezone.timedelta(days=60)
        trainer = TrainerFactory(last_changed_password=two_months_ago)

        # The password security policy is off.
        with self.settings(PASSWORD_EXTRA_SECURITY=False):
            self.assertFalse(trainer.check_password_expiry())

        # The password security policy is on, and the password is not too old..
        with self.settings(PASSWORD_EXTRA_SECURITY=True, PASSWORD_TTL=90):
            self.assertFalse(trainer.check_password_expiry())

        # The password security policy is on, and the password is too old.
        with self.settings(PASSWORD_EXTRA_SECURITY=True, PASSWORD_TTL=30):
            self.assertTrue(trainer.check_password_expiry())

    def test_is_trainer_method(self):
        """Test the ``is_trainer`` method of the ``MTPUser`` model"""

        # a trainer is a trainer
        trainer = TrainerFactory()
        self.assertTrue(trainer.is_trainer())

        # a trainee is not
        trainee = TraineeFactory()
        self.assertFalse(trainee.is_trainer())

    def test_is_trainee_method(self):
        """Test the ``is_trainee`` method of the ``MTPUser`` model"""

        # a trainee is a trainee
        trainee = TraineeFactory()
        self.assertTrue(trainee.is_trainee())

        # a trainer is not
        trainer = TrainerFactory()
        self.assertFalse(trainer.is_trainee())

    def test_managed_groups(self):
        """Test the ``managed_groups`` property of the ``MTPUser`` model"""

        # The trainer is not managing any groups
        self.assertEqual(
            self.trainee.managed_groups.count(),
            0
        )

        # The admin is managing two groups
        self.assertEqual(
            self.admin.managed_groups.count(),
            2
        )


class PasswordSecurityTestCase(MTPTestCase):

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

    def test_password_security_policy(self):
        """
        Test the navigation across the CMS when the password security policy
        is active.
        """

        # So, we have a trainer who hasn't changed their password in a while...
        now = timezone.now()
        two_months_ago = now - timezone.timedelta(days=60)
        self.trainer.last_changed_password = two_months_ago
        self.trainer.save()

        # They want to go to the CMS index.
        index_url = str(settings.ROOT_URL)
        password_change_url = reverse('accounts:password_change')
        password_change_done_url = reverse('password_change_done')

        # If the password security policy is off, no problem.
        with self.settings(PASSWORD_EXTRA_SECURITY=False):
            response = self.client.get(index_url)
            self.assertEqual(response.status_code, 200)

        # If the policy is on and the password is fresh enough, no problem.
        with self.settings(PASSWORD_EXTRA_SECURITY=True, PASSWORD_TTL=90):
            response = self.client.get(index_url)
            self.assertEqual(response.status_code, 200)

        # If the policy is on and the password is old, redirect!
        with self.settings(PASSWORD_EXTRA_SECURITY=True, PASSWORD_TTL=30):
            response = self.client.get(index_url)
            self.assertEqual(response.status_code, 302)

            actual_redirect_path = url_to_path(response.url)
            expected_redirect_path = password_change_url
            self.assertEqual(actual_redirect_path, expected_redirect_path)

            # Now, if they want to go anywhere else, they are redirected to
            # the password change form.
            response = self.client.get(index_url)
            self.assertEqual(response.status_code, 302)

            actual_redirect_path = url_to_path(response.url)
            expected_redirect_path = password_change_url
            self.assertEqual(actual_redirect_path, expected_redirect_path)

            # The only way to proceed is to actually fill out the form.
            response = self.client.post(password_change_url, {
                'old_password': DEFAULT_PASSWORD,
                'new_password1': SECURE_PASSWORD,
                'new_password2': SECURE_PASSWORD,
            })
            self.assertEqual(response.status_code, 302)

            actual_redirect_path = url_to_path(response.url)
            expected_redirect_path = password_change_done_url
            self.assertEqual(actual_redirect_path, expected_redirect_path)

            # Once this is done, they can navigate freely through the cms.
            response = self.client.get(index_url)
            self.assertEqual(response.status_code, 200)


class SuperuserDecoratorTestCase(TestCase):
    """Test case that tests the ``test_if_superuser`` static method.

    Notes
    -----
    All the tests in this test case simply define a user to set in ``self.request.user``,
    then call ``self.call_view()`` to get the response, and then they can check the status code.

    """

    class TestView(generic.View):
        """View used for our tests of the base view."""

        def get(self, request, *args, **kwargs):
            """Simply return a text response."""
            return HttpResponse("OK", content_type="text/plain")

        @method_decorator(user_passes_test(MTPUser.test_if_superuser))
        def dispatch(self, request, *args, **kwargs):
            """Dispatch method overridden to be decorated."""
            return super().dispatch(request, *args, **kwargs)

    def setUp(self):
        """Create a request object for each test."""

        super().setUp()
        self.request = RequestFactory().get('/fake/path/not/used')

    def call_view(self):
        """Call the view with the current request.

        Returns
        -------
        django.http.HttpResponse
            The response returned by the view.

        """

        return self.TestView.as_view()(self.request)

    def test_non_authenticated_users_should_be_rejected(self):
        """Test that a request with an user that is not authenticated is not allowed."""

        self.request.user = AnonymousUser()
        self.assertNotEqual(self.call_view().status_code, 200)

    def test_non_active_users_should_be_rejected(self):
        """Test that a request with an authenticated, but not active, user is not allowed."""

        self.request.user = AdminFactory(is_active=False)
        self.assertNotEqual(self.call_view().status_code, 200)

    def test_superusers_should_be_allowed(self):
        """Test that a user authenticated as an active superuser is allowed."""

        self.request.user = AdminFactory()
        self.assertEqual(self.call_view().status_code, 200)

    def test_trainers_should_be_rejected(self):
        """Test that a user authenticated as an active trainer is not allowed."""

        self.request.user = TrainerFactory()
        self.assertNotEqual(self.call_view().status_code, 200)

    def test_trainee_should_be_rejected(self):
        """Test that a user authenticated as an active trainer is not allowed."""

        self.request.user = TraineeFactory()
        self.assertNotEqual(self.call_view().status_code, 200)


class SuperuserOrTrainerDecoratorTestCase(TestCase):
    """Test case that tests the ``test_if_superuser_or_trainer`` static method.

    Notes
    -----
    All the tests in this test case simply define a user to set in ``self.request.user``,
    then call ``self.call_view()`` to get the response, and then they can check the status code.

    """

    class TestView(generic.View):
        """View used for our tests of the base view."""

        def get(self, request, *args, **kwargs):
            """Simply return a text response."""
            return HttpResponse("OK", content_type="text/plain")

        @method_decorator(user_passes_test(MTPUser.test_if_superuser_or_trainer))
        def dispatch(self, request, *args, **kwargs):
            """Dispatch method overridden to be decorated."""
            return super().dispatch(request, *args, **kwargs)

    def setUp(self):
        """Create a request object for each test."""

        super().setUp()
        self.request = RequestFactory().get('/fake/path/not/used')

    def call_view(self):
        """Call the view with the current request.

        Returns
        -------
        django.http.HttpResponse
            The response returned by the view.

        """

        return self.TestView.as_view()(self.request)

    def test_non_authenticated_users_should_be_rejected(self):
        """Test that a request with an user that is not authenticated is not allowed."""

        self.request.user = AnonymousUser()
        self.assertNotEqual(self.call_view().status_code, 200)

    def test_non_active_users_should_be_rejected(self):
        """Test that a request with an authenticated, but not active, user is not allowed."""

        self.request.user = AdminFactory(is_active=False)
        self.assertNotEqual(self.call_view().status_code, 200)

    def test_superusers_should_be_allowed(self):
        """Test that a user authenticated as an active superuser is allowed."""

        self.request.user = AdminFactory()
        self.assertEqual(self.call_view().status_code, 200)

    def test_trainers_should_be_allowed(self):
        """Test that a user authenticated as an active trainer is allowed."""

        self.request.user = TrainerFactory()
        self.assertEqual(self.call_view().status_code, 200)

    def test_trainee_should_be_rejected(self):
        """Test that a user authenticated as an active trainer is not allowed."""

        self.request.user = TraineeFactory()
        self.assertNotEqual(self.call_view().status_code, 200)


class TrainerDecoratorTestCase(TestCase):
    """Test case that tests the ``test_if_trainer`` static method.

    Notes
    -----
    All the tests in this test case simply define a user to set in ``self.request.user``,
    then call ``self.call_view()`` to get the response, and then they can check the status code.

    """

    class TestView(generic.View):
        """View used for our tests of the base view."""

        def get(self, request, *args, **kwargs):
            """Simply return a text response."""
            return HttpResponse("OK", content_type="text/plain")

        @method_decorator(user_passes_test(MTPUser.test_if_trainer))
        def dispatch(self, request, *args, **kwargs):
            """Dispatch method overridden to be decorated."""
            return super().dispatch(request, *args, **kwargs)

    def setUp(self):
        """Create a request object for each test."""

        super().setUp()
        self.request = RequestFactory().get('/fake/path/not/used')

    def call_view(self):
        """Call the view with the current request.

        Returns
        -------
        django.http.HttpResponse
            The response returned by the view.

        """

        return self.TestView.as_view()(self.request)

    def test_non_authenticated_users_should_be_rejected(self):
        """Test that a request with an user that is not authenticated is not allowed."""

        self.request.user = AnonymousUser()
        self.assertNotEqual(self.call_view().status_code, 200)

    def test_non_active_users_should_be_rejected(self):
        """Test that a request with an authenticated, but not active, user is not allowed."""

        self.request.user = AdminFactory(is_active=False)
        self.assertNotEqual(self.call_view().status_code, 200)

    def test_superusers_should_be_allowed(self):
        """Test that a user authenticated as an active superuser is allowed.

        Because we assume that, in the factory, an admin is a trainer.

        TODO: check this.

        """

        self.request.user = AdminFactory()
        self.assertEqual(self.call_view().status_code, 200)

    def test_trainers_should_be_allowed(self):
        """Test that a user authenticated as an active trainer is allowed."""

        self.request.user = TrainerFactory()
        self.assertEqual(self.call_view().status_code, 200)

    def test_trainee_should_be_rejected(self):
        """Test that a user authenticated as an active trainer is not allowed."""

        self.request.user = TraineeFactory()
        self.assertNotEqual(self.call_view().status_code, 200)


class UserDetailsViewTestCase(MTPTestCase):
    """Test case that tests the ``user_details`` view."""

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

        super().setUpTestData()

        # We will need another trainee
        cls.trainee_2 = TraineeFactory()

    def test_trainee_profile_not_self_should_be_rejected(self):
        """Test that a trainee should not be allowed to edit another trainee's profile."""

        self.login_as_trainee()
        response = self.client.get(self.trainee_2.get_edit_url())

        self.assertEqual(
            response.status_code,
            404
        )

    def test_trainee_profile_is_self_should_be_accepted(self):
        """Test that a trainee should be allowed to edit his own profile."""

        self.login_as_trainee()

        response = self.client.get(self.trainee.get_edit_url())

        self.assertEqual(
            response.status_code,
            200
        )

    def test_admin_should_be_accepted(self):
        """Test that a trainer should be allowed to edit all profiles."""

        self.login_as_admin()

        # The admin can edit the trainee's details...
        response = self.client.get(self.trainee.get_edit_url())
        self.assertEqual(
            response.status_code,
            200
        )

        # ...and also the trainer's details
        response = self.client.get(self.trainer.get_edit_url())
        self.assertEqual(
            response.status_code,
            200
        )


class UserWelcomeViewTestCase(MTPTestCase):
    """Test case that tests the ``user_welcome`` view."""

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

        cls.url = cls.trainee.get_welcome_url()

    def test_trainee_should_be_rejected(self):
        """Test that a trainee should be not allowed to resent a welcome mail."""
        self.login_as_trainee()
        response = self.client.get(self.url, {}, follow=True)
        self.assertRedirects(response, settings.LOGIN_URL + '?next=' + self.trainee.get_list_url())

    def test_trainer_should_be_accepted(self):
        """Test that a trainer should be allowed to resent a welcome mail."""
        self.login_as_trainer()
        response = self.client.get(self.url, {}, follow=True)
        messages = list(response.context['messages'])

        self.assertEqual(len(messages), 1)
        self.assertEqual(messages[0].level, MSG.SUCCESS)
        self.assertEqual(response.status_code, 200)

    def test_admin_should_be_accepted(self):
        """Test that a admin should be allowed to resent a welcome mail."""
        self.login_as_admin()
        response = self.client.get(self.url, {}, follow=True)
        messages = list(response.context['messages'])

        self.assertEqual(len(messages), 1)
        self.assertEqual(messages[0].level, MSG.SUCCESS)
        self.assertEqual(response.status_code, 200)


class GroupDeleteViewTestCase(MTPTestCase):
    """Test case that tests the ``GroupDeleteView`` view."""

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

        cls.group = GroupFactory()
        GroupRoleFactory(group=cls.group, user=cls.trainees[1])

        cls.group_delete_url = cls.group.get_delete_url()

    def test_trainee_should_be_rejected(self):
        """Test that a trainee should't be allowed to soft delete group."""
        self.login_as_trainee()
        response = self.client.post(self.group_delete_url, {})

        self.assertEqual(response.status_code, 404)

    def test_trainer_should_be_rejected(self):
        """Test that a trainer should't be allowed to soft delete group."""
        self.login_as_trainer()
        response = self.client.post(self.group_delete_url, {})

        self.assertEqual(response.status_code, 404)

    def test_admin_should_be_accepted(self):
        """Test that a admin should be allowed to soft delete group."""
        self.login_as_admin()
        response = self.client.post(self.group_delete_url, {}, follow=True)
        messages = list(response.context['messages'])

        self.assertEqual(len(messages), 1)
        self.assertEqual(messages[0].level, MSG.SUCCESS)
        self.assertEqual(response.status_code, 200)

    def test_group_is_soft_deleted(self):
        """Test that the group is soft deleted."""
        self.login_as_admin()
        self.client.post(self.group_delete_url, {})

        up_group = Group.objects.get(id=self.group.id)

        # Verify if group is soft-deleted
        self.assertTrue(up_group.is_deleted)
        # Verify if group's member is not deleted
        self.assertFalse(up_group.members.first().is_deleted)

class MTPUserNotificationTestcase(MTPTestCase):
    """Test cases for the email notifications of the ``MTPUser`` model."""

    @unittest.skip('The view to download the trainee content dont exist anymore')
    def test_user_welcome_from_user_add_view(self):
        """Test that the ``user_add`` view should send a mail to the
        given user."""

        # We need to be logged in as a trainer
        self.login_as_trainer()

        # Call the view
        self.client.post(reverse('accounts:user-create'), {
            'username': 'test',
            'first_name': 'test',
            'last_name': 'test',
            'email': 'test@test.com',
            'natural_language': 'en'
        })

        # The mail subject is correct
        self.assertEqual(
            mail.outbox[0].subject,
            "Welcome to the Magency digital training platform."
        )

        # We should get a new mail to send
        self.assertEqual(len(mail.outbox), 1)

        # The recipient is the user
        self.assertEqual('test@test.com', mail.outbox[0].to[0])

    @unittest.skip('The view to download the trainee content dont exist anymore')
    def test_user_welcome_from_importing_file(self):
        """Test that the importing of a file with a new user should send
        a mail to this user."""

        # We import a new user from a file
        user_file = finders.find('users_import.xls')
        xls_import_trainee(user_file, commit=True)

        # We should get a new mail to send
        self.assertEqual(len(mail.outbox), 1)

    @unittest.skip('The view to download the trainee content dont exist anymore')
    def test_user_welcome_from_welcome_view(self):
        """Test that the ``user_welcome`` view should send a mail to the
        given user."""

        # We need to be logged in as a trainer
        self.login_as_trainer()

        # Call the view
        self.client.post(self.trainee.get_welcome_url())

        # We should get a new mail to send
        self.assertEqual(len(mail.outbox), 1)

        # The mail subject is correct
        self.assertEqual(
            mail.outbox[0].subject,
            "Welcome to the Magency digital training platform."
        )

        # The password is not in the body
        self.assertIn(
            "********",
            mail.outbox[0].body
        )

        # The recipient is the user
        self.assertEqual(self.trainee.email, mail.outbox[0].to[0])


class GroupRoleModelTestCase(MTPTestCase):
    """Test cases for the through ``GroupRole`` model."""

    def test_grouprole_create(self):
        """Test the creation of a new ``GroupRole`` model."""

        # We create a new trainer
        trainer = TrainerFactory()

        # Create a new group
        group = GroupFactory()

        # Add the trainer to a new group
        GroupRoleFactory(user=trainer, group=group)

        # The trainer is in the group
        self.assertIn(
            group,
            trainer.mgroups.all()
        )

        # He is a ``MEMBER`` of the group
        self.assertEqual(
            trainer.group_roles.get(group=group).role,
            GroupRole.ROLES.MEMBER
        )

        # We can't re-add the trainer to the group
        with self.assertRaises(IntegrityError):
            GroupRoleFactory(user=trainer, group=group)

    def test_grouprole_manager(self):
        """Test the unicity of a given group user role ``MANAGER``."""

        # Create a trainer
        trainer = TrainerFactory()

        # Create a new group
        group = GroupFactory()

        # Add the trainer to a new group, as a ``MANAGER``.
        group_role = GroupRoleFactory(user=trainer, group=group, role=GroupRole.ROLES.MANAGER)
        # Subsequant call to the save method don't raise an exception
        group_role.save()

        # The manager is a member
        self.assertEqual(
            group.members.count(),
            1
        )

        # The trainer is the manager of this group
        self.assertEqual(
            group.manager_member,
            trainer
        )

        # Adding another manager to the group will raise a ``ValidationError``.
        with self.assertRaises(ValidationError):
            GroupRoleFactory(user=TrainerFactory(),
                group=group, role=GroupRole.ROLES.MANAGER)


class LoginUserViewTestCase(MTPTestCase):
    """Test the ``LoginUserView`` view"""

    def get_url(self, next_url=None):
        """Get the login url, with or without a ``next`` argument."""

        url = '%(login_url)s'
        if next_url is not None:
            url += '?next=%(next_url)s'

        return url % {
            'login_url': settings.LOGIN_URL,
            'next_url': next_url
        }

    def test_failed_login(self):
        """A failed login should not redirect."""

        response = self.client.post(self.get_url(), {
            'username': 'wronglogin',
            'password': 'wrongpassword'
        })

        self.assertEqual(response.status_code, 200)
        self.assertTrue(
            response.context['form'].has_error(NON_FIELD_ERRORS, 'invalid_login')
        )

    def test_successful_login_without_next_url(self):
        """A successful login without ``next`` argument should redirect to the root url."""

        response = self.client.post(self.get_url(), {
            'username': self.trainer.username,
            'password': DEFAULT_PASSWORD
        })

        self.assertRedirects(response, str(settings.ROOT_URL))

    def test_successful_login_with_empty_next_url(self):
        """A successful login with empty ``next`` argument should redirect to the root url."""

        response = self.client.post(self.get_url(next_url=''), {
            'username': self.trainer.username,
            'password': DEFAULT_PASSWORD
        })

        self.assertRedirects(response, str(settings.ROOT_URL))

    def test_successful_login_with_invalid_next_url(self):
        """A successful login with invalid ``next`` argument should redirect to the root url.

        An invalid url is an url with a bad scheme+host

        """
        response = self.client.post(self.get_url(next_url='http://foo.com/bar'), {
            'username': self.trainer.username,
            'password': DEFAULT_PASSWORD
        })

        self.assertRedirects(response, str(settings.ROOT_URL))

    def test_successful_login_with_valid_next_url(self):
        """A successful login with valid ``next`` argument should redirect to this ``next`` url."""

        next_url = reverse('trainings:training-list')

        response = self.client.post(self.get_url(next_url=next_url), {
            'username': self.trainer.username,
            'password': DEFAULT_PASSWORD
        })

        self.assertRedirects(response, next_url)

    def test_trainee_should_not_be_able_to_login(self):
        """A user who belongs to the ``trainee`` groups should not be able to login to the cms"""

        response = self.client.post(
            self.get_url(), {
                'username': self.trainee.username,
                'password': DEFAULT_PASSWORD
                }
        )

        self.assertEqual(response.status_code, 200)
        self.assertTrue(
            response.context['form'].has_error(NON_FIELD_ERRORS, 'is_trainee')
        )


class UserListViewTestCase(MTPTestCase):
    """Test cases for the ``UserListView`` class"""

    @classmethod
    def setUpClass(cls):
        """Store the url of the user list"""

        super().setUpClass()

        cls.user_list_url = MTPUser.get_list_url()

    def test_only_superusers_should_have_access(self):
        """Test that only the superusers can access the user list view"""

        # Trainee should be rejected...
        self.login_as_trainee()
        response = self.client.get(self.user_list_url)

        self.assertEquals(response.status_code, 302)

        # ...So are the trainers...
        self.login_as_trainer()
        response = self.client.get(self.user_list_url)

        self.assertEquals(response.status_code, 302)

        # ...But the superusers have access
        self.login_as_admin()
        response = self.client.get(self.user_list_url)

        self.assertEquals(response.status_code, 200)

    def test_filters_should_filter_the_user_list(self):
        """Test that using the filters should correctly filter the list of user"""

        self.login_as_admin()

        # Unfiltered list
        response = self.client.get(self.user_list_url)
        self.assertEquals(len(response.context['mtpuser_list']), 4)  # 3 trainees + 1 trainer

        # Filter by username
        url = '%s?search=%s' % (self.user_list_url, self.trainee.username)
        response = self.client.get(url)
        self.assertEquals(len(response.context['mtpuser_list']), 1)  # 1 trainee

        # Filter by user group
        url = '%s?mgroups=%s' % (self.user_list_url, self.trainer_group.pk)
        response = self.client.get(url)
        self.assertEquals(len(response.context['mtpuser_list']), 1)  # 1 trainer

        # Filter by ``is_deleted``
        url = '%s?is_deleted=%s' % (self.user_list_url, 1)
        response = self.client.get(url)
        self.assertEquals(len(response.context['mtpuser_list']), 0)  # 0 user soft-deleted

        # soft-delete a user
        self.trainee.is_deleted = True
        self.trainee.save()

        response = self.client.get(url)
        self.assertEquals(len(response.context['mtpuser_list']), 1)  # 1 user soft-deleted
