import random
import datetime
import string
import json

from django.urls import reverse
from django.test import TestCase
from django.utils import timezone

from rest_framework.test import APITestCase
from rest_framework import status

from core.factories import CustomUserFactory
from core.utils import get_tokens_for_user

from . import models
from . import factories


# Model Tests
class CountryTest(TestCase):
    """Country model tests
    """

    def setUp(self):
        """Tests setup
        """
        self.model = models.Country

    def test_content(self):
        """Test content correctly set
        """
        # Define instance data
        data = {
            'name': 'country name _test_ data'
        }

        # Create instance
        instance = self.model.objects.create(**data)

        # Assert content correctly set
        for field in data.keys():
            self.assertEqual(getattr(instance, field), data[field])


class RegionTest(TestCase):
    """Region model tests
    """

    def setUp(self):
        """Tests setup
        """
        self.model = models.Region

    def test_content(self):
        """Test content correctly set
        """
        # Define instance data
        data = {
            'name': 'region name _test_ data',
            'country': factories.CountryFactory(),
        }

        # Create instance
        instance = self.model.objects.create(**data)

        # Assert content correctly set
        for field in data.keys():
            self.assertEqual(getattr(instance, field), data[field])


class ProvinceTest(TestCase):
    """Province model tests
    """

    def setUp(self):
        """Tests setup
        """
        self.model = models.Province

    def test_content(self):
        """Test content correctly set
        """
        # Define instance data
        data = {
            'name': 'region name _test_ data',
            'region': factories.RegionFactory(),
        }

        # Create instance
        instance = self.model.objects.create(**data)

        # Assert content correctly set
        for field in data.keys():
            self.assertEqual(getattr(instance, field), data[field])


class CityTest(TestCase):
    """City model tests
    """

    def setUp(self):
        """Tests setup
        """
        self.model = models.City

    def test_content(self):
        """Test content correctly set
        """
        # Define instance data
        data = {
            'name': 'city name _test_ data',
            'province': factories.ProvinceFactory(),
        }

        # Create instance
        instance = self.model.objects.create(**data)

        # Assert content correctly set
        for field in data.keys():
            self.assertEqual(getattr(instance, field), data[field])



# ViewSet Tests
class CountryViewSetTest(APITestCase):
    """Country viewset tests
    """

    def setUp(self):
        """Tests setup
        """
        self.endpoint = '/api/v1/countries/'
        self.factory = factories.CountryFactory
        self.model = models.Country
        # create user
        self.password = ''.join(random.choices(string.ascii_uppercase, k = 10))
        self.user = CustomUserFactory(email="test@mail.com", password=self.password, is_active=True)

    def test_anon_user_cannot_create_country(self):
        """Not logged-in user cannot create new country
        """
        # Query endpoint
        response = self.client.post(self.endpoint, data={})

        # Assert access is forbidden
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def test_anon_user_cannot_modify_existing_country(self):
        """Not logged-in user cannot modify existing country
        """
        # Create instance
        instance = self.factory()

        # Query endpoint
        url = self.endpoint + f'{instance.pk}/'
        response = self.client.put(url, {}, format='json')

        # Assert forbidden code
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def test_anon_user_cannot_delete_existing_country(self):
        """Not logged-in user cannot delete existing country
        """
        # Create instances
        instance = self.factory()

        # Query endpoint
        url = self.endpoint + f'{instance.pk}/'
        response = self.client.delete(url)

        # Assert instance still exists on db
        self.assertTrue(self.model.objects.get(id=instance.pk))

    def test_anon_user_can_list_country(self):
        """Not logged-in user can't read country
        """
        # Request list
        response = self.client.get(self.endpoint)

        # Assert access is forbidden
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_auth_user_can_list_country(self):
        """Regular logged-in user can list country
        """
        # Create instances
        instances = [self.factory() for n in range(random.randint(1,5))]

        # Authenticate user
        token = get_tokens_for_user(self.user)
        self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}")

        # Request list
        response = self.client.get(self.endpoint)

        # Assert access is allowed
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        # Assert all instances are returned
        self.assertEqual(len(instances), len(response.data))

    def test_auth_user_cannot_create_country(self):
        """Regular logged-in user can create new country
        """

        # Authenticate user
        token = get_tokens_for_user(self.user)
        self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}")

        # Query endpoint
        response = self.client.post(self.endpoint, data={})

        # Assert access is forbidden
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_auth_user_cannot_modify_existing_country(self):
        """Regular logged-in user can modify existing country
        """
        # Create instances
        instance = self.factory()

        # Query endpoint
        url = self.endpoint + f'{instance.pk}/'

        # Authenticate user
        token = get_tokens_for_user(self.user)
        self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}")

        # Query endpoint
        response = self.client.post(url, data={})

        # Assert access is forbidden
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_auth_user_cannot_delete_existing_country(self):
        """Regular logged-in user can delete existing country
        """
        # Create instances
        instance = self.factory()

        # Authenticate user
        token = get_tokens_for_user(self.user)
        self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}")

        # Query endpoint
        url = self.endpoint + f'{instance.pk}/'
        # Query endpoint
        response = self.client.post(url, data={})

        # Assert access is forbidden
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)


class RegionViewSetTest(APITestCase):
    """Region viewset tests
    """

    def setUp(self):
        """Tests setup
        """
        self.endpoint = '/api/v1/regions/'
        self.factory = factories.RegionFactory
        self.model = models.Region
        # create user
        self.password = ''.join(random.choices(string.ascii_uppercase, k = 10))
        self.user = CustomUserFactory(email="test@mail.com", password=self.password, is_active=True)

    def test_not_logged_user_cannot_create_instance(self):
        """Not logged-in user cannot create new region
        """
        # Query endpoint
        response = self.client.post(self.endpoint, data={})

        # Assert access is forbidden
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def test_not_logged_user_cannot_modify_existing_instance(self):
        """Not logged-in user cannot modify existing region
        """
        # Create instance
        instance = self.factory()

        # Query endpoint
        url = f"{self.endpoint}{instance.pk}/"
        response = self.client.put(url, {}, format='json')

        # Assert forbidden code
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def test_not_logged_user_cannot_delete_existing_instance(self):
        """Not logged-in user cannot delete existing region
        """
        # Create instances
        instance = self.factory()

        # Query endpoint
        url = f"{self.endpoint}{instance.pk}"
        response = self.client.delete(url)

        # assert response status code
        self.assertEqual(response.status_code, status.HTTP_301_MOVED_PERMANENTLY)
        # Assert instance still exists on db
        self.assertTrue(self.model.objects.get(id=instance.pk))

    def test_not_logged_user_can_list_instance(self):
        """Not logged-in user can list instances
        """
        # Create instances
        instances = [self.factory() for n in range(random.randint(1,5))]
        # Request list
        response = self.client.get(self.endpoint)

        # Assert access is allowed
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        # Assert all instances are returned
        self.assertEqual(len(instances), len(response.data))

    def test_logged_user_can_list_instances(self):
        """Regular logged-in user can list instances
        """
        # Create instances
        instances = [self.factory() for n in range(random.randint(1,5))]

        # Authenticate user
        token = get_tokens_for_user(self.user)
        self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}")

        # Request list
        response = self.client.get(self.endpoint)

        # Assert access is allowed
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        # Assert all instances are returned
        self.assertEqual(len(instances), len(response.data))

    def test_logged_user_cannot_create_instance(self):
        """Regular logged-in user can create new region
        """
        # Authenticate user
        token = get_tokens_for_user(self.user)
        self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}")

        # Query endpoint
        response = self.client.post(self.endpoint, data={})

        # Assert access is forbidden
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_logged_user_cannot_modify_existing_instance(self):
        """Regular logged-in user can modify existing region
        """
        # Create instances
        instance = self.factory()

        # Query endpoint
        url = self.endpoint + f'{instance.pk}/'

        # Authenticate user
        token = get_tokens_for_user(self.user)
        self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}")

        # Query endpoint
        response = self.client.post(url, data={})

        # Assert access is forbidden
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_logged_user_cannot_delete_existing_instance(self):
        """Regular logged-in user can delete existing region
        """
        # Create instances
        instance = self.factory()

        # Authenticate user
        token = get_tokens_for_user(self.user)
        self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}")

        # Query endpoint
        url = self.endpoint + f'{instance.pk}/'
        # Query endpoint
        response = self.client.post(url, data={})

        # Assert access is forbidden
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)


class ProvinceViewSetTest(APITestCase):
    """Province viewset tests
    """

    def setUp(self):
        """Tests setup
        """
        self.endpoint = '/api/v1/provinces/'
        self.factory = factories.ProvinceFactory
        self.model = models.Province
        # create user
        self.password = ''.join(random.choices(string.ascii_uppercase, k = 10))
        self.user = CustomUserFactory(email="test@mail.com", password=self.password, is_active=True)

    def test_not_logged_user_cannot_create_instance(self):
        """Not logged-in user cannot create new instance
        """
        # Query endpoint
        response = self.client.post(self.endpoint, data={})

        # Assert access is forbidden
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def test_not_logged_user_cannot_modify_existing_instance(self):
        """Not logged-in user cannot modify existing instance
        """
        # Create instance
        instance = self.factory()

        # Query endpoint
        url = f"{self.endpoint}{instance.pk}/"
        response = self.client.put(url, {}, format='json')

        # Assert forbidden code
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def test_not_logged_user_cannot_delete_existing_instance(self):
        """Not logged-in user cannot delete existing instance
        """
        # Create instances
        instance = self.factory()

        # Query endpoint
        url = f"{self.endpoint}{instance.pk}"
        response = self.client.delete(url)

        # assert response status code
        self.assertEqual(response.status_code, status.HTTP_301_MOVED_PERMANENTLY)
        # Assert instance still exists on db
        self.assertTrue(self.model.objects.get(id=instance.pk))

    def test_not_logged_user_can_list_instance(self):
        """Not logged-in user can list instances
        """
        # Create instances
        instances = [self.factory() for n in range(random.randint(1,5))]
        # Request list
        response = self.client.get(self.endpoint)

        # Assert access is allowed
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        # Assert all instances are returned
        self.assertEqual(len(instances), len(response.data))

    def test_logged_user_can_list_instances(self):
        """Regular logged-in user can list instances
        """
        # Create instances
        instances = [self.factory() for n in range(random.randint(1,5))]

        # Authenticate user
        token = get_tokens_for_user(self.user)
        self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}")

        # Request list
        response = self.client.get(self.endpoint)

        # Assert access is allowed
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        # Assert all instances are returned
        self.assertEqual(len(instances), len(response.data))

    def test_logged_user_cannot_create_instance(self):
        """Regular logged-in user can create new instance
        """
        # Authenticate user
        token = get_tokens_for_user(self.user)
        self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}")

        # Query endpoint
        response = self.client.post(self.endpoint, data={})

        # Assert access is forbidden
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_logged_user_cannot_modify_existing_instance(self):
        """Regular logged-in user can modify existing instance
        """
        # Create instances
        instance = self.factory()

        # Query endpoint
        url = self.endpoint + f'{instance.pk}/'

        # Authenticate user
        token = get_tokens_for_user(self.user)
        self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}")

        # Query endpoint
        response = self.client.post(url, data={})

        # Assert access is forbidden
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_logged_user_cannot_delete_existing_instance(self):
        """Regular logged-in user can delete existing instance
        """
        # Create instances
        instance = self.factory()

        # Authenticate user
        token = get_tokens_for_user(self.user)
        self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}")

        # Query endpoint
        url = self.endpoint + f'{instance.pk}/'
        # Query endpoint
        response = self.client.post(url, data={})

        # Assert access is forbidden
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)


class CityViewSetTest(APITestCase):
    """City viewset tests
    """

    def setUp(self):
        """Tests setup
        """
        self.endpoint = '/api/v1/cities/'
        self.factory = factories.CityFactory
        self.model = models.City
        # create user
        self.password = ''.join(random.choices(string.ascii_uppercase, k = 10))
        self.user = CustomUserFactory(email="test@mail.com", password=self.password, is_active=True)

    def test_not_logged_user_cannot_create_city(self):
        """Not logged-in user cannot create new city
        """
        # Query endpoint
        response = self.client.post(self.endpoint, data={})

        # Assert access is forbidden
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def test_not_logged_user_cannot_modify_existing_city(self):
        """Not logged-in user cannot modify existing city
        """
        # Create instance
        instance = self.factory()

        # Query endpoint
        url = f"{self.endpoint}{instance.pk}/"
        response = self.client.put(url, {}, format='json')

        # Assert forbidden code
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def test_not_logged_user_cannot_delete_existing_city(self):
        """Not logged-in user cannot delete existing city
        """
        # Create instances
        instance = self.factory()

        # Query endpoint
        url = f"{self.endpoint}{instance.pk}/"
        response = self.client.delete(url)

        # Assert instance still exists on db
        self.assertTrue(self.model.objects.get(id=instance.pk))

    def test_not_logged_user_can_list_city(self):
        """Not logged-in user can't read city
        """
        # Create instances
        instances = [self.factory() for n in range(random.randint(1,5))]

        # Request list
        response = self.client.get(self.endpoint)

        # Assert access is forbidden
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        # Assert all instances are returned
        self.assertEqual(len(instances), len(response.data))

    def test_logged_user_can_list_instances(self):
        """Regular logged-in user can list instances
        """
        # Create instances
        instances = [self.factory() for n in range(random.randint(1,5))]

        # Authenticate user
        token = get_tokens_for_user(self.user)
        self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}")

        # Request list
        response = self.client.get(self.endpoint)

        # Assert access is allowed
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        # Assert all instances are returned
        self.assertEqual(len(instances), len(response.data))

    def test_logged_user_cannot_create_instance(self):
        """Regular logged-in user can create new instance
        """
        # Authenticate user
        token = get_tokens_for_user(self.user)
        self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}")

        # Query endpoint
        response = self.client.post(self.endpoint, data={})

        # Assert access is forbidden
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_logged_user_cannot_modify_existing_instance(self):
        """Regular logged-in user can modify existing instance
        """
        # Create instances
        instance = self.factory()

        # Query endpoint
        url = self.endpoint + f'{instance.pk}/'

        # Authenticate user
        token = get_tokens_for_user(self.user)
        self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}")

        # Query endpoint
        response = self.client.post(url, data={})

        # Assert access is forbidden
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_logged_user_cannot_delete_existing_instance(self):
        """Regular logged-in user can delete existing instance
        """
        # Create instances
        instance = self.factory()

        # Authenticate user
        token = get_tokens_for_user(self.user)
        self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}")

        # Query endpoint
        url = self.endpoint + f'{instance.pk}/'
        # Query endpoint
        response = self.client.post(url, data={})

        # Assert access is forbidden
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

