"""This modules holds decorators tests for ``accounts`` app."""

from django.contrib.auth.decorators import user_passes_test
from django.contrib.auth.models import AnonymousUser
from django.http import HttpResponse
from django.test import RequestFactory, TestCase
from django.utils.decorators import method_decorator
from django.views import generic

from ..factories import (
    AdminFactory,
    TraineeFactory,
    TrainerFactory,
)
from ..models import MTPUser


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."""

        # pylint: disable=unused-argument,missing-param-doc,missing-type-doc

        def get(self, *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."""

        # pylint: disable=unused-argument,missing-param-doc,missing-type-doc

        def get(self, *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."""

        # pylint: disable=unused-argument,missing-param-doc,missing-type-doc
        def get(self, *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)
