"""This module contains the tests for the v2 api-related views of the accounts app"""

from django.core.urlresolvers import reverse
from django.test.utils import override_settings

from rest_framework import status

from mtp.apps.accounts.constants import (
    INTERNAL_GROUP_TRAINEE,
    INTERNAL_GROUP_WHOSWHO
)
from mtp.apps.accounts.factories import AvatarFactory
from mtp.apps.accounts.models import MTPUser
from mtp.apps.api.tests.test_cases import MTPAPITestCase


class UserViewSetTestCase(MTPAPITestCase):
    """Test cases for the ``UserViewSet`` viewset."""

    @classmethod
    def setUpClass(cls):
        """Get the url of the view"""

        super().setUpClass()

        cls.me_url = reverse('apiv2:user-me')

    def create_user_through_api(self, username):
        """Create an user through api.

        Parameters
        ----------
        username: str
            The username of new user.
            We allow to edit it because this field must be unique.

        Returns
        -------
        Response
            The returned response by api.

        """

        return self.client.post(reverse('apiv2:user-list'), {
            'first_name': 'fake_firstname',
            'last_name': 'fake_lastname',
            'email': 'fake_email@email.com',
            'username': username or 'fake_user',
            'password': 'fake_password',
            'language': 'fr',
        })

    def update_user_through_api(self):
        """Update an user through api.

        Returns
        -------
        Response
            The returned response by api.

        """

        return self.client.patch(
            reverse('apiv2:user-detail', kwargs={'pk': self.trainee.pk}),
            {
                'username': 'new_fake_usersane',
                'password': 'new_fake_password',
            }
        )

    def test_route_should_return_the_serialized_authenticated_user(self):
        """Test that the route should return the serialized authenticated user"""

        # Add an avatar on the trainee
        self.trainee.photo = AvatarFactory()
        self.trainee.save()

        self.login_as_trainee()
        response = self.client.get(self.me_url)

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(
            response.data,
            {
                'id': self.trainee.pk,
                'username': self.trainee.username,
                'first_name': self.trainee.first_name,
                'last_name': self.trainee.last_name,
                'email': self.trainee.email,
                'language': self.trainee.natural_language,
                'profile_picture': self.trainee.photo.img.url,
                'external_id': self.trainee.external_id,
                'groups': [
                    group.name
                    for group in self.trainee.mgroups.non_deleted()
                ],
            }
        )

    def test_route_should_reject_unauthenticated_user(self):
        """Test that the route should reject an unauthenticated user"""

        response = self.client.get(self.me_url)

        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def test_user_creation(self):
        """Test the user creation through the API."""

        # A trainer should be able to create a new user
        self.login_as_trainer()
        response = self.create_user_through_api(username='fake_user1')

        self.assertEqual(response.status_code, status.HTTP_201_CREATED)

        # A anonymous user should be able to create this account
        self.client.force_authenticate(user=None)
        response = self.create_user_through_api(username='fake_user2')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)

        # We should have a new user, with correct data
        new_user = MTPUser.objects.get(username='fake_user2')

        self.assertEqual(new_user.first_name, 'fake_firstname')
        self.assertEqual(new_user.last_name, 'fake_lastname')
        self.assertEqual(new_user.email, 'fake_email@email.com')
        self.assertEqual(new_user.natural_language, 'fr')

        # The new user should be able to login with his password
        self.assertTrue(
            self.client.login(username='fake_user2', password='fake_password')
        )

        # The new user should have his default groups correctly set
        self.assertCountEqual(
            new_user.mgroups.values_list('name', flat=True),
            [INTERNAL_GROUP_TRAINEE, INTERNAL_GROUP_WHOSWHO]
        )

        # The username should be transformed to lowercase during the registration
        self.client.force_authenticate(user=None)

        username = 'UsernameWithCapitalLetters'
        response = self.create_user_through_api(username=username)
        new_user = MTPUser.objects.get(username=username.lower())

    @override_settings(INSTANCE_IS_SLAVE=True, INSTANCE_IS_MASTER=False)
    def test_user_creation_should_set_the_created_from_attribute_on_slave(self):
        """Test that the ``created_from`` attribute should be correctly set"""

        self.create_user_through_api(username='fake_user1')
        new_user = MTPUser.objects.get(username='fake_user1')

        self.assertEqual(new_user.created_from, MTPUser.ORIGINS.SLAVE_REGISTRATION)

    @override_settings(INSTANCE_IS_SLAVE=False, INSTANCE_IS_MASTER=True)
    def test_user_creation_should_set_the_created_from_attribute_on_master(self):
        """Test that the ``created_from`` attribute should be correctly set"""

        self.create_user_through_api(username='fake_user1')
        new_user = MTPUser.objects.get(username='fake_user1')

        self.assertEqual(new_user.created_from, MTPUser.ORIGINS.MASTER_REGISTRATION)

    def test_user_update(self):
        """Test the user update through the API."""

        # A trainer should be able to update an user
        self.login_as_trainer()
        response = self.update_user_through_api()
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        # An anonymous user shouldn't be able to update an user
        self.client.force_authenticate(user=None)
        response = self.update_user_through_api()
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def test_user_list_num_queries(self):
        """
        Test the user-list endpoint should issue no more than 6 queries.

        Notes
        -----

          The queries are:
          - select accounts_mtpuser <...> where id=xxx (this is the request.user query)
          - SAVEPOINT
          - select accounts_mtpuser <...> where is_deleted" = false (the actual query)
          - select accounts_grouprole <...> (the first prefetch)
          - select accounts_avatar (the second prefetch)
          - RELEASE SAVEPOINT

        """

        url = reverse('apiv2:user-list')
        self.login_as_trainer()
        with self.assertNumQueries(6):
            self.client.get(url)
