import random
import string
import json

from django.test import TestCase

from rest_framework.test import APITestCase
from rest_framework import status

from companies.factories import ValidatedCompanyFactory, CompanyFactory
from companies.models import Company

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


# Create your tests here.
class CompanyViewSetTest(APITestCase):
    """CompanyViewset tests
    """

    def setUp(self):
        """Tests setup
        """
        self.endpoint = '/api/v1/companies/'
        self.factory = ValidatedCompanyFactory
        self.model = Company
        # 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)

    # anonymous user
    def test_anon_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_anon_user_cannot_modify_existing_instance(self):
        """Not logged-in user cannot modify existing instance
        """
        # 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_instance(self):
        """Not logged-in user cannot delete existing instance
        """
        # Create instances
        instance = self.factory()

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

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

    def test_anon_user_can_list_instance(self):
        """Not logged-in user can list instance
        """
        # Request list
        response = self.client.get(self.endpoint)

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

    def test_anon_user_can_paginate_instances(self):
        """Not logged-in user can paginate instances
        """
        instances = [self.factory() for n in range(12)]
        # Request list
        url = f"{self.endpoint}?limit=5&offset=10"
        response = self.client.get(url)

        # Assert access is allowed
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        # assert only 2 instances in response
        payload = response.json()
        self.assertEquals(2, len(payload['results']))

    def test_anon_user_can_filter_tags(self):
        # create instances
        expected_instance = [
            self.factory(tags='ropa'),
            self.factory(tags='tejidos, ropa')
        ]
        unexpected_instance = [
            self.factory(tags="zapatos, azules"),
            self.factory(tags="xxl")
        ]
        # prepare url
        url = f"{self.endpoint}?tags=ropa"

        # Request list
        response = self.client.get(url)
        payload = response.json()

        # Assert access is granted
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        # Assert number of instnaces in response
        self.assertEquals(len(expected_instance), len(payload))

    # authenticated user
    def test_logged_user_can_list_instance(self):
        """Regular logged-in user can list instance
        """
        # 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_can_create_instance(self):
        """Regular logged-in user can create new instance
        """
        # Define request data
        data = {
            'cif': 'qwerewq',
            'company_name': 'qwerewq',
            'short_name': 'qwerewq',
            'web_link': 'http://qwerewq.com',
            'shop': True,
            'shop_link': 'http://qwerewq.com',
            'platform': 'PRESTASHOP',
            'email': 'test@email.com',
            'logo': None,
            'city': None,
            'address': 'qwer qewr 5',
            'geo': {'longitude': 1.0, 'latitude': 1.0},
            'phone': '1234',
            'mobile': '4321',
            'other_phone': '41423',
            'description': 'dfgfdgdfg',
            'shop_rss_feed': 'http://qwerewq.com',
            'sale_terms': 'tewrnmfew f ewfrfew ewewew f',
            'shipping_cost': '12.25',
            'sync': False
        }

        # 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=data, format='json')

        # Assert endpoint returns created status
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)

        # Assert instance exists on db
        self.assertTrue(self.model.objects.get(id=response.data['id']))

    def test_logged_user_can_modify_own_instance(self):
        """Regular logged-in user can modify existing instance
        """
        # Create instances
        instance = self.factory()
        # make our user the creator
        instance.creator = self.user
        instance.save()

        # Define request data
        data = {
            'cif': 'qwerewq',
            'company_name': 'qwerewq',
            'short_name': 'qwerewq',
            'web_link': 'http://qwerewq.com',
            'shop': True,
            'shop_link': 'http://qwerewq.com',
            'platform': 'PRESTASHOP',
            'email': 'test@email.com',
            'logo': None,
            'city': None,
            'address': 'qwer qewr 5',
            'geo': None,
            'phone': '1234',
            'mobile': '4321',
            'other_phone': '41423',
            'description': 'dfgfdgdfg',
            'shop_rss_feed': 'http://qwerewq.com',
            'sale_terms': 'tewrnmfew f ewfrfew ewewew f',
            'shipping_cost': '12.25',
            'sync': False
        }

        # 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}/'
        response = self.client.put(url, data, format='json')

        # Assert endpoint returns OK code
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        # Assert instance has been modified
        for key in data:
            self.assertEqual(data[key], response.data[key])

    def test_logged_user_cannot_modify_other_users_instance(self):
        """Regular logged-in user cannot modify other user's 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}/'
        response = self.client.put(url, data={}, format='json')

        # Assert endpoint returns OK code
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_logged_user_cannot_delete_other_users_instance(self):
        """Regular logged-in user cannot delete other user's 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}/'
        response = self.client.delete(url, format='json')

        # Assert endpoint returns OK code
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_logged_user_can_delete_own_instance(self):
        """Regular logged-in user can delete existing instance
        """
        # Create instances
        instance = self.factory()
        # make our user the creator
        instance.creator = self.user
        instance.save()

        # 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}/'
        response = self.client.delete(url)

        # assert 204 no content
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
        # Assert instance doesn't exists anymore on db
        self.assertFalse(self.model.objects.filter(id=instance.pk).exists())

    # functionalities
    def test_descending_order(self):
        instances = [self.factory(), self.factory()]

        # 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))
        # check order
        self.assertTrue(response.data[0]['id'] > response.data[1]['id'])

    # TODO: test email_manager action


class MyCompanyViewTest(APITestCase):
    """CompanyViewset tests
    """

    def setUp(self):
        """Tests setup
        """
        self.endpoint = '/api/v1/my_company/'
        self.factory = ValidatedCompanyFactory
        self.model = Company
        # create user
        self.email = f"user@mail.com"
        self.password = ''.join(random.choices(string.ascii_uppercase, k = 10))
        self.user = CustomUserFactory(email=self.email, is_active=True)
        self.user.set_password(self.password)
        self.user.save()

    def test_auth_user_gets_data(self):
        # create instance
        company = CompanyFactory()
        self.user.company = company
        self.user.save()

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

        # Query endpoint
        response = self.client.get(self.endpoint)
        # Assert forbidden code
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        payload = response.json()
        self.assertEquals(payload['company']['id'], company.id)

    def test_anon_user_cannot_access(self):
        # send in request
        response = self.client.get(self.endpoint)

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


class RandomCompanySampleTest(APITestCase):

    def setUp(self):
        """Tests setup
        """
        self.endpoint = '/api/v1/companies/sample/'
        self.factory = ValidatedCompanyFactory
        self.model = Company
        # create user
        self.email = f"user@mail.com"
        self.password = ''.join(random.choices(string.ascii_uppercase, k = 10))
        self.user = CustomUserFactory(email=self.email, is_active=True)
        self.user.set_password(self.password)
        self.user.save()

    def test_default_behavior(self):
        """Expect 6 instances as default
        """
        # create instances
        instances = [ self.factory() for i in range(20)]

        # Query endpoint
        response = self.client.get(self.endpoint)
        payload = response.json()

        # Assert forbidden code
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEquals(6, len(payload))
        # test IDs not correlative (eventually it could be, because it's random)
        self.assertTrue(payload[0]['id'] != (payload[1]['id'] + 1))

    def test_custom_size_behavior(self):
        """Expect response size equal to parameter value
        """
        # create instances
        instances = [ self.factory() for i in range(20)]

        # Query endpoint
        size = 10
        url = f"{self.endpoint}?size={size}"
        response = self.client.get(url)
        payload = response.json()

        # Assert forbidden code
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEquals(size, len(payload))
        # test IDs not correlative (eventually it could be, because it's random)
        self.assertTrue(payload[0]['id'] != (payload[1]['id'] + 1))


class AdminCompanyViewSetTest(APITestCase):

    def setUp(self):
        """Tests setup
        """
        self.endpoint = '/api/v1/admin_companies/'
        self.factory = CompanyFactory
        self.model = Company
        # create user
        self.email = f"user@mail.com"
        self.password = ''.join(random.choices(string.ascii_uppercase, k = 10))
        self.user = CustomUserFactory(email=self.email, is_active=True)
        self.user.set_password(self.password)
        self.user.save()

    def test_anon_user_cannot_access(self):
        instance = self.factory()
        url = f"{self.endpoint}{instance.id}/"
        # GET
        response = self.client.get(self.endpoint)
        # check response
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
        # POST
        response = self.client.post(self.endpoint, data={})
        # check response
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
        # PUT
        response = self.client.get(url, data={})
        # check response
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
        # delete
        response = self.client.get(url)
        # check response
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

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

        instance = self.factory()
        url = f"{self.endpoint}{instance.id}/"
        # GET
        response = self.client.get(self.endpoint)
        # check response
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
        # POST
        response = self.client.post(self.endpoint, data={})
        # check response
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
        # PUT
        response = self.client.get(url, data={})
        # check response
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
        # delete
        response = self.client.get(url)
        # check response
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_admin_user_can_list(self):
        # make user site amdin
        self.user.role = 'SITE_ADMIN'
        self.user.save()

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

        # create instances
        instance = [self.factory() for i in range(random.randint(1,5))]
        # query endpoint
        response = self.client.get(self.endpoint)

        # assertions
        self.assertEquals(response.status_code, 200)
        payload = response.json()
        self.assertEquals(len(instance), len(payload))

    def test_admin_user_can_get_details(self):
        # make user site amdin
        self.user.role = 'SITE_ADMIN'
        self.user.save()

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

        # create instances
        instance = self.factory()
        url = f"{self.endpoint}{instance.id}/"
        # query endpoint
        response = self.client.get(url)

        # assertions
        self.assertEquals(response.status_code, 200)
        payload = response.json()
        self.assertEquals(instance.id, payload['id'])

    def test_admin_can_create_instance(self):
        # make user site amdin
        self.user.role = 'SITE_ADMIN'
        self.user.save()

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

        # create instances
        data = {
            'short_name': 'test_compnay short _name',
        }
        # query endpoint
        response = self.client.post(self.endpoint, data=data)

        # assertions
        self.assertEquals(response.status_code, 201)
        payload = response.json()
        self.assertEquals(data['short_name'], payload['short_name'])

    def test_admin_can_update_instance(self):
        # make user site amdin
        self.user.role = 'SITE_ADMIN'
        self.user.save()

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

        # create instance
        instance = self.factory()
        url = f"{self.endpoint}{instance.id}/"

        # data
        data = {
            'short_name': 'test_compnay short _name',
        }
        # query endpoint
        response = self.client.put(url, data=data)

        # assertions
        self.assertEquals(response.status_code, 200)
        payload = response.json()
        self.assertEquals(data['short_name'], payload['short_name'])

    def test_admin_can_delete_instance(self):
        # make user site amdin
        self.user.role = 'SITE_ADMIN'
        self.user.save()

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

        # create instance
        instance = self.factory()
        url = f"{self.endpoint}{instance.id}/"

        # query endpoint
        response = self.client.delete(url)

        # assertions
        self.assertEquals(response.status_code, 204)


class CompanySearchViewSet(APITestCase):

    def setUp(self):
        """Tests setup
        """
        self.endpoint = '/api/v1/search/companies/'
        self.factory = ValidatedCompanyFactory
        self.model = Company
        # create user
        self.email = f"user@mail.com"
        self.password = ''.join(random.choices(string.ascii_uppercase, k = 10))
        self.user = CustomUserFactory(email=self.email, is_active=True)
        self.user.set_password(self.password)
        self.user.save()

    def test_anon_user_can_search_company_name(self):
        company_name = 'Test asdfas name'
        # create instance
        instance = self.factory(company_name=company_name)

        # query
        url = f"{self.endpoint}?search=asdfas"
        response = self.client.get(url)

        # assertions
        self.assertEquals(response.status_code, 200)

        payload = response.json()
        self.assertEquals(len(payload), 1)

    def test_anon_user_can_search_short_name(self):
        short_name = 'Test werqwe name'
        # create instance
        instance = self.factory(short_name=short_name)

        # query
        url = f"{self.endpoint}?search=werqwe"
        response = self.client.get(url)

        # assertions
        self.assertEquals(response.status_code, 200)

        payload = response.json()
        self.assertEquals(len(payload), 1)

