from django.test import TestCase
from django.contrib.auth import get_user_model
from django.urls import reverse

from rest_framework.test import APIClient
from rest_framework import status


class TestUserApi(TestCase):
    USER_API_URL = reverse('user:create')
    TOKEN_API_URL = reverse('user:token')
    ME_URL = reverse('user:me')


class TestPublicUsersAPI(TestUserApi):

    def setUp(self):
        self.client = APIClient()

    def test_create_new_user(self):
        """Test creating user with valid payload is successful"""
        payload = {
            "name": "<NAME>",
            "email": "<EMAIL>",
            "password": "<PASSWORD>!"
        }
        response = self.client.post(self.USER_API_URL, payload)

        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        user = get_user_model().objects.get(**response.data)
        self.assertTrue(user.check_password(payload['password']))
        self.assertNotIn('password', response.data)

    def test_create_existing_user(self):
        """Test creating user that already exists fails"""
        payload = {
            "name": "<NAME>",
            "email": "<EMAIL>",
            "password": "<PASSWORD>!"
        }
        get_user_model().objects.create_user(**payload)

        response = self.client.post(self.USER_API_URL, payload)

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

    def test_create_user_password_too_short(self):
        """Test user creation fails if password is shorter than 6 characters"""
        payload = {
            "name": "<NAME>",
            "email": "<EMAIL>",
            "password": "12"
        }

        response = self.client.post(self.USER_API_URL, payload)

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        user_exists = get_user_model().objects.filter(
            email=payload['email']
        ).exists()
        self.assertFalse(user_exists)

    def test_create_token_for_user(self):
        """Test that a token is created for the user"""
        payload = {'email': '<EMAIL>', 'password': '<PASSWORD>'}
        get_user_model().objects.create_user(**payload)
        response = self.client.post(self.TOKEN_API_URL, payload)

        self.assertIn('token', response.data)
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_create_token_invalid_credentials(self):
        """Test that token is not created if invalid credentials are given"""
        username = '<EMAIL>'
        good_payload = {'email': username, 'password': '<PASSWORD>'}
        get_user_model().objects.create_user(**good_payload)

        wrong_payload = {'email': username, 'password': '<PASSWORD>'}
        response = self.client.post(self.TOKEN_API_URL, wrong_payload)

        self.assertNotIn('token', response.data)
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test_create_token_no_user(self):
        """Test that token is not created if user doesn't exist"""
        payload = {'email': '<EMAIL>', 'password': '<PASSWORD>'}

        response = self.client.post(self.TOKEN_API_URL, payload)

        self.assertNotIn('token', response.data)
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test_create_token_no_password(self):
        """Test that email and password are required"""
        payload = {'email': '<EMAIL>', 'password': ''}

        response = self.client.post(self.TOKEN_API_URL, payload)

        self.assertNotIn('token', response.data)
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test_retrieve_user_unauthorized(self):
        """Test that authentication is required for users"""
        response = self.client.get(self.ME_URL)

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


class TestPrivateUserApi(TestUserApi):
    """Test API requests that require authentication"""

    def setUp(self):
        payload = {
            "name": "<NAME>",
            "email": "<EMAIL>",
            "password": "<PASSWORD>!"
        }
        self.user = get_user_model().objects.create_user(**payload)
        self.client = APIClient()
        self.client.force_authenticate(user=self.user)

    def test_retrieve_profile_success(self):
        """Test retrieving the profile of a logged in user"""
        response = self.client.get(self.ME_URL)

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        payload = {
            "name": self.user.name,
            "email": self.user.email,
        }
        self.assertEqual(response.data, payload)

    def test_post_me_not_allowed(self):
        """Test that POST is not allowed in the me URL"""
        response = self.client.post(self.ME_URL, {})
        self.assertEqual(
            response.status_code,
            status.HTTP_405_METHOD_NOT_ALLOWED,
        )

    def test_update_profile_success(self):
        """Test updating the user profile for an authenticated user"""
        payload = {
            'name': '<NAME>',
            'password': '<PASSWORD>',
        }
        response = self.client.patch(self.ME_URL, payload)
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        self.user.refresh_from_db()
        self.assertEqual(self.user.name, payload['name'])
        self.assertTrue(self.user.check_password(payload['password']))

from django.test import TestCase
from django.contrib.auth import get_user_model
from django.urls import reverse

from rest_framework.test import APIClient
from rest_framework import status


class TestUserApi(TestCase):
    USER_API_URL = reverse('user:create')
    TOKEN_API_URL = reverse('user:token')
    ME_URL = reverse('user:me')


class TestPublicUsersAPI(TestUserApi):

    def setUp(self):
        self.client = APIClient()

    def test_create_new_user(self):
        """Test creating user with valid payload is successful"""
        payload = {
            "name": "<NAME>",
            "email": "<EMAIL>",
            "password": "<PASSWORD>!"
        }
        response = self.client.post(self.USER_API_URL, payload)

        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        user = get_user_model().objects.get(**response.data)
        self.assertTrue(user.check_password(payload['password']))
        self.assertNotIn('password', response.data)

    def test_create_existing_user(self):
        """Test creating user that already exists fails"""
        payload = {
            "name": "<NAME>",
            "email": "<EMAIL>",
            "password": "<PASSWORD>!"
        }
        get_user_model().objects.create_user(**payload)

        response = self.client.post(self.USER_API_URL, payload)

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

    def test_create_user_password_too_short(self):
        """Test user creation fails if password is shorter than 6 characters"""
        payload = {
            "name": "<NAME>",
            "email": "<EMAIL>",
            "password": "12"
        }

        response = self.client.post(self.USER_API_URL, payload)

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        user_exists = get_user_model().objects.filter(
            email=payload['email']
        ).exists()
        self.assertFalse(user_exists)

    def test_create_token_for_user(self):
        """Test that a token is created for the user"""
        payload = {'email': '<EMAIL>', 'password': '<PASSWORD>'}
        get_user_model().objects.create_user(**payload)
        response = self.client.post(self.TOKEN_API_URL, payload)

        self.assertIn('token', response.data)
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_create_token_invalid_credentials(self):
        """Test that token is not created if invalid credentials are given"""
        username = '<EMAIL>'
        good_payload = {'email': username, 'password': '<PASSWORD>'}
        get_user_model().objects.create_user(**good_payload)

        wrong_payload = {'email': username, 'password': '<PASSWORD>'}
        response = self.client.post(self.TOKEN_API_URL, wrong_payload)

        self.assertNotIn('token', response.data)
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test_create_token_no_user(self):
        """Test that token is not created if user doesn't exist"""
        payload = {'email': '<EMAIL>', 'password': '<PASSWORD>'}

        response = self.client.post(self.TOKEN_API_URL, payload)

        self.assertNotIn('token', response.data)
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test_create_token_no_password(self):
        """Test that email and password are required"""
        payload = {'email': '<EMAIL>', 'password': ''}

        response = self.client.post(self.TOKEN_API_URL, payload)

        self.assertNotIn('token', response.data)
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test_retrieve_user_unauthorized(self):
        """Test that authentication is required for users"""
        response = self.client.get(self.ME_URL)

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


class TestPrivateUserApi(TestUserApi):
    """Test API requests that require authentication"""

    def setUp(self):
        payload = {
            "name": "<NAME>",
            "email": "<EMAIL>",
            "password": "<PASSWORD>!"
        }
        self.user = get_user_model().objects.create_user(**payload)
        self.client = APIClient()
        self.client.force_authenticate(user=self.user)

    def test_retrieve_profile_success(self):
        """Test retrieving the profile of a logged in user"""
        response = self.client.get(self.ME_URL)

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        payload = {
            "name": self.user.name,
            "email": self.user.email,
        }
        self.assertEqual(response.data, payload)

    def test_post_me_not_allowed(self):
        """Test that POST is not allowed in the me URL"""
        response = self.client.post(self.ME_URL, {})
        self.assertEqual(
            response.status_code,
            status.HTTP_405_METHOD_NOT_ALLOWED,
        )

    def test_update_profile_success(self):
        """Test updating the user profile for an authenticated user"""
        payload = {
            'name': '<NAME>',
            'password': '<PASSWORD>',
        }
        response = self.client.patch(self.ME_URL, payload)
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        self.user.refresh_from_db()
        self.assertEqual(self.user.name, payload['name'])
        self.assertTrue(self.user.check_password(payload['password']))

Test creating user with valid payload is successful Test creating user that already exists fails Test user creation fails if password is shorter than 6 characters Test that a token is created for the user Test that token is not created if invalid credentials are given Test that token is not created if user doesn't exist Test that email and password are required Test that authentication is required for users Test API requests that require authentication Test retrieving the profile of a logged in user Test that POST is not allowed in the me URL Test updating the user profile for an authenticated user