from datetime import timedelta
from unittest.mock import patch

import requests
from django.conf import settings
from django.contrib.sessions.middleware import SessionMiddleware
from django.test import TestCase, RequestFactory
from django.utils import timezone
from graphene.test import Client

from api.schema_public import schema
from core.factories import UserFactory
from core.hep_client import HepClient
from core.tests.mock_hep_data_factory import MockResponse, ME_DATA, VALID_STUDENT_ORDERS, VALID_TEACHERS_ORDERS, \
    NOT_CONFIRMED_ME
from users.factories import LicenseFactory
from users.models import Role, User, SchoolClass, License, UserData

TOKEN = 'abcd12345!'

MYSKILLBOX_GROUP_ID_ME_DATA = ME_DATA.copy()
MYSKILLBOX_GROUP_ID_ME_DATA['group_id'] = settings.HEP_MYSKILLBOX_GROUP_ID


class LoginTests(TestCase):
    def setUp(self):
        self.user = UserFactory(username=ME_DATA['id'], email=ME_DATA['id'])
        Role.objects.create_default_roles()
        self.teacher_role = Role.objects.get_default_teacher_role()

        request = RequestFactory().post('/')

        # adding session
        middleware = SessionMiddleware()
        middleware.process_request(request)
        request.session.save()
        self.client = Client(schema=schema, context_value=request)

    def make_login_mutation(self, token):
        mutation = '''
            mutation Login($input: LoginInput!){
              login(input: $input) {
                success
                message
              }
            }
        '''

        return self.client.execute(mutation, variables={
            'input': {
                'tokenInput': token
            }
        })

    @patch.object(HepClient, 'customer_me', return_value=ME_DATA)
    def test_user_can_login_with_local_user_and_valid_local_license(self, me_mock):

        self.user.hep_id = ME_DATA['id']
        self.user.save()

        now = timezone.now()
        expiry_date = now + timedelta(100)
        LicenseFactory(expire_date=expiry_date, licensee=self.user, for_role=self.teacher_role).save()

        result = self.make_login_mutation(TOKEN)

        self.assertTrue(result.get('data').get('login').get('success'))
        self.assertTrue(self.user.is_authenticated)

        reloaded_user = User.objects.get(pk=self.user.id)
        self.assertEqual(reloaded_user.license_expiry_date, expiry_date.date())

    @patch.object(HepClient, 'customer_me', return_value=ME_DATA)
    def test_user_can_login_with_local_user_and_valid_local_licenses(self, me_mock):

        self.user.hep_id = ME_DATA['id']
        self.user.save()

        now = timezone.now()
        expiry_date1 = now + timedelta(100)
        expiry_date2 = now + timedelta(110)

        LicenseFactory(expire_date=expiry_date1, licensee=self.user, for_role=self.teacher_role).save()
        LicenseFactory(expire_date=expiry_date2, licensee=self.user, for_role=self.teacher_role).save()

        result = self.make_login_mutation(TOKEN)

        self.assertTrue(result.get('data').get('login').get('success'))
        self.assertTrue(self.user.is_authenticated)

        reloaded_user = User.objects.get(pk=self.user.id)
        self.assertEqual(reloaded_user.license_expiry_date, expiry_date2.date())

    @patch.object(HepClient, 'customer_me', return_value=ME_DATA)
    def test_user_data_is_synced_on_login(self, me_mock):

        old_mail = 'aschi@iterativ.ch'

        self.user.hep_id = ME_DATA['id']
        self.user.email = old_mail
        self.user.username = old_mail
        self.user.save()

        now = timezone.now()
        expiry_date = now + timedelta(365)
        LicenseFactory(expire_date=expiry_date, licensee=self.user, for_role=self.teacher_role).save()

        result = self.make_login_mutation(TOKEN)

        user = User.objects.get(hep_id=self.user.hep_id)

        self.assertEqual(user.username, ME_DATA['email'])
        self.assertEqual(user.email, ME_DATA['email'])
        self.assertTrue(result.get('data').get('login').get('success'))
        self.assertTrue(self.user.is_authenticated)

    @patch.object(HepClient, 'customer_me', return_value=ME_DATA)
    def test_user_can_login_with_local_user_and_valid_local_licenses(self, me_mock):

        self.user.hep_id = ME_DATA['id']
        self.user.save()

        now = timezone.now()
        expiry_date1 = now + timedelta(365)
        expiry_date2 = now + timedelta(366)

        LicenseFactory(expire_date=expiry_date1, licensee=self.user, for_role=self.teacher_role).save()
        LicenseFactory(expire_date=expiry_date2, licensee=self.user, for_role=self.teacher_role).save()

        result = self.make_login_mutation(TOKEN)

        self.assertTrue(result.get('data').get('login').get('success'))
        self.assertTrue(self.user.is_authenticated)

    @patch.object(HepClient, '_customer_orders', return_value=VALID_TEACHERS_ORDERS)
    @patch.object(HepClient, 'customer_me', return_value=ME_DATA)
    @patch.object(HepClient, 'fetch_admin_token', return_value={'token': 'AABBCCDDEE**44566'})
    def test_teacher_can_login_with_remote_user_and_remote_license(self, order_mock, me_mock, admin_token_mock):
        result = self.make_login_mutation(TOKEN)

        user = User.objects.get(email=ME_DATA['email'])

        user_role_key = user.user_roles.get(user=user).role.key
        self.assertEqual(user_role_key, Role.objects.TEACHER_KEY)

        license = License.objects.get(licensee=user)
        self.assertEqual(license.for_role.key, Role.objects.TEACHER_KEY)

        school_class = SchoolClass.objects.get(users__in=[user])
        self.assertIsNotNone(school_class)

        self.assertTrue(result.get('data').get('login').get('success'))
        self.assertTrue(self.user.is_authenticated)

        try:
            UserData.objects.get(user=user)
            self.fail('LoginTests.test_teacher_can_login_with_remote_user_and_remote_license: Userdata should not exist')
        except:
            pass

    @patch.object(HepClient, '_customer_orders', return_value=VALID_TEACHERS_ORDERS)
    @patch.object(HepClient, 'customer_me', return_value=MYSKILLBOX_GROUP_ID_ME_DATA)
    @patch.object(HepClient, 'fetch_admin_token', return_value={'token': 'AABBCCDDEE**44566'})
    def test_teacher_can_login_with_remote_user_and_remote_license_with_myskillbox_group_id(self, order_mock, me_mock, admin_token_mock):
        self.make_login_mutation(TOKEN)
        user = User.objects.get(email=MYSKILLBOX_GROUP_ID_ME_DATA['email'])
        try:
            UserData.objects.get(user=user)
        except:
            self.fail('LoginTests.test_teacher_can_login_with_remote_user_and_remote_license: Userdata should exist')

    @patch.object(HepClient, '_customer_orders', return_value=VALID_STUDENT_ORDERS)
    @patch.object(HepClient, 'customer_me', return_value=ME_DATA)
    @patch.object(HepClient, 'fetch_admin_token', return_value={'token': 'AABBCCDDEE**44566'})
    def test_student_can_login_with_remote_user_and_remote_license(self, order_mock, me_mock, admin_token_mock):

        result = self.make_login_mutation(TOKEN)
        user = User.objects.get(email=ME_DATA['email'])

        user_role_key = user.user_roles.get(user=user).role.key
        self.assertEqual(user_role_key, Role.objects.STUDENT_KEY)

        license = License.objects.get(licensee=user)
        self.assertEqual(license.for_role.key, Role.objects.STUDENT_KEY)

        self.assertTrue(result.get('data').get('login').get('success'))
        self.assertTrue(self.user.is_authenticated)

    @patch.object(requests, 'post', return_value=MockResponse(401))
    def test_user_with_no_login_cannot_login(self, post_mock):
        result = self.make_login_mutation('some')

        self.assertEqual(result.get('errors')[0].get('message'), 'invalid_credentials')

    @patch.object(HepClient, 'is_email_verified', return_value=False)
    @patch.object(HepClient, 'customer_me', return_value=ME_DATA)
    def test_user_with_unconfirmed_email_cannot_login(self, me_mock, post_mock):
        result = self.make_login_mutation(TOKEN)

        User.objects.get(email=ME_DATA['email'])
        self.assertEqual(result.get('errors')[0].get('message'), 'email_not_verified')

    @patch.object(HepClient, 'myskillbox_product_for_customer', return_value=None)
    @patch.object(HepClient, 'customer_me', return_value=ME_DATA)
    @patch.object(HepClient, 'fetch_admin_token', return_value={'token': 'AABBCCDDEE**44566'})
    def test_user_can_login_without_license(self, me_mock, product_mock, admin_token_mock):
        result = self.make_login_mutation(TOKEN)

        self.assertTrue(result.get('data').get('login').get('success'))
        self.assertEqual(result.get('data').get('login').get('message'), 'no_valid_license')
        self.assertTrue(self.user.is_authenticated)

    @patch.object(HepClient, 'myskillbox_product_for_customer', return_value=None)
    @patch.object(HepClient, 'customer_me', return_value=ME_DATA)
    @patch.object(HepClient, 'fetch_admin_token', return_value={'token': 'AABBCCDDEE**44566'})
    def test_user_can_login_local_license_invalid(self, product_mock, me_mock, admin_token_mock):
        now = timezone.now()
        expiry_date = now - timedelta(1)
        LicenseFactory(expire_date=expiry_date, licensee=self.user, for_role=self.teacher_role).save()

        result = self.make_login_mutation(TOKEN)

        self.assertTrue(result.get('data').get('login').get('success'))
        self.assertEqual(result.get('data').get('login').get('message'), 'no_valid_license')
        self.assertTrue(self.user.is_authenticated)

    @patch.object(HepClient, 'customer_me', return_value=NOT_CONFIRMED_ME)
    def test_user_can_login_with_unconfirmed_email(self, me_mock):
        result = self.make_login_mutation(TOKEN)

        self.assertEqual(result.get('errors')[0].get('message'), 'email_not_verified')

    @patch.object(requests, 'get', return_value=MockResponse(500))
    def test_user_gets_notified_if_server_error(self, post_mock):
        result = self.make_login_mutation(TOKEN)

        self.assertEqual(result.get('errors')[0].get('message'), 'unknown_error')
