import json

import responses
from allauth.socialaccount.models import SocialApp
from allauth.socialaccount.providers.facebook.provider import GRAPH_API_URL
from django.contrib.auth import get_user_model
from django.contrib.sites.models import Site
from django.test import TestCase
from django.test.utils import override_settings
from rest_framework import status

from .mixins import TestsMixin


try:
    from django.urls import reverse
except ImportError:
    from django.core.urlresolvers import reverse


@override_settings(ROOT_URLCONF='tests.urls')
class TestSocialAuth(TestsMixin, TestCase):

    USERNAME = 'person'
    PASS = 'person'
    EMAIL = 'person1@world.com'
    REGISTRATION_DATA = {
        'username': USERNAME,
        'password1': PASS,
        'password2': PASS,
        'email': EMAIL,
    }

    def setUp(self):
        self.init()

        social_app = SocialApp.objects.create(
            provider='facebook',
            name='Facebook',
            client_id='123123123',
            secret='321321321',
        )

        twitter_social_app = SocialApp.objects.create(
            provider='twitter',
            name='Twitter',
            client_id='11223344',
            secret='55667788',
        )

        site = Site.objects.get_current()
        social_app.sites.add(site)
        twitter_social_app.sites.add(site)
        self.graph_api_url = GRAPH_API_URL + '/me'
        self.twitter_url = 'http://twitter.com/foobarme'

    @responses.activate
    def test_failed_social_auth(self):
        # fake response
        responses.add(
            responses.GET,
            self.graph_api_url,
            body='',
            status=400,
            content_type='application/json',
        )

        payload = {
            'access_token': 'abc123',
        }
        self.post(self.fb_login_url, data=payload, status_code=400)

    @responses.activate
    def test_social_auth(self):
        # fake response for facebook call
        resp_body = {
            'id': '123123123123',
            'first_name': 'John',
            'gender': 'male',
            'last_name': 'Smith',
            'link': 'https://www.facebook.com/john.smith',
            'locale': 'en_US',
            'name': 'John Smith',
            'timezone': 2,
            'updated_time': '2014-08-13T10:14:38+0000',
            'username': 'john.smith',
            'verified': True,
        }

        responses.add(
            responses.GET,
            self.graph_api_url,
            body=json.dumps(resp_body),
            status=200,
            content_type='application/json',
        )

        users_count = get_user_model().objects.all().count()
        payload = {
            'access_token': 'abc123',
        }

        self.post(self.fb_login_url, data=payload, status_code=200)
        self.assertIn('key', self.response.json.keys())
        self.assertEqual(get_user_model().objects.all().count(), users_count + 1)

        # make sure that second request will not create a new user
        self.post(self.fb_login_url, data=payload, status_code=200)
        self.assertIn('key', self.response.json.keys())
        self.assertEqual(get_user_model().objects.all().count(), users_count + 1)

    def _twitter_social_auth(self):
        # fake response for twitter call
        resp_body = {
            'id': '123123123123',
        }

        responses.add(
            responses.GET,
            'https://api.twitter.com/1.1/account/verify_credentials.json',
            body=json.dumps(resp_body),
            status=200,
            content_type='application/json',
        )

        users_count = get_user_model().objects.all().count()
        payload = {
            'access_token': 'abc123',
            'token_secret': '1111222233334444',
        }

        self.post(self.tw_login_url, data=payload)

        self.assertIn('key', self.response.json.keys())
        self.assertEqual(get_user_model().objects.all().count(), users_count + 1)

        # make sure that second request will not create a new user
        self.post(self.tw_login_url, data=payload, status_code=200)
        self.assertIn('key', self.response.json.keys())
        self.assertEqual(get_user_model().objects.all().count(), users_count + 1)

    @responses.activate
    @override_settings(SOCIALACCOUNT_AUTO_SIGNUP=True)
    def test_twitter_social_auth(self):
        self._twitter_social_auth()

    @responses.activate
    @override_settings(SOCIALACCOUNT_AUTO_SIGNUP=False)
    def test_twitter_social_auth_without_auto_singup(self):
        self._twitter_social_auth()

    @responses.activate
    def test_twitter_social_auth_request_error(self):
        # fake response for twitter call
        resp_body = {
            'id': '123123123123',
        }

        responses.add(
            responses.GET,
            'https://api.twitter.com/1.1/account/verify_credentials.json',
            body=json.dumps(resp_body),
            status=400,
            content_type='application/json',
        )

        users_count = get_user_model().objects.all().count()
        payload = {
            'access_token': 'abc123',
            'token_secret': '1111222233334444',
        }

        self.post(self.tw_login_url, data=payload, status_code=400)
        self.assertNotIn('key', self.response.json.keys())
        self.assertEqual(get_user_model().objects.all().count(), users_count)

    @responses.activate
    def test_twitter_social_auth_no_view_in_context(self):
        # fake response for twitter call
        resp_body = {
            'id': '123123123123',
        }

        responses.add(
            responses.GET,
            'https://api.twitter.com/1.1/account/verify_credentials.json',
            body=json.dumps(resp_body),
            status=400,
            content_type='application/json',
        )

        users_count = get_user_model().objects.all().count()
        payload = {
            'access_token': 'abc123',
            'token_secret': '1111222233334444',
        }

        self.post(self.tw_login_no_view_url, data=payload, status_code=400)
        self.assertEqual(get_user_model().objects.all().count(), users_count)

    @responses.activate
    def test_twitter_social_auth_no_adapter(self):
        # fake response for twitter call
        resp_body = {
            'id': '123123123123',
        }

        responses.add(
            responses.GET,
            'https://api.twitter.com/1.1/account/verify_credentials.json',
            body=json.dumps(resp_body),
            status=400,
            content_type='application/json',
        )

        users_count = get_user_model().objects.all().count()
        payload = {
            'access_token': 'abc123',
            'token_secret': '1111222233334444',
        }

        self.post(self.tw_login_no_adapter_url, data=payload, status_code=400)
        self.assertEqual(get_user_model().objects.all().count(), users_count)

    @responses.activate
    @override_settings(
        ACCOUNT_EMAIL_VERIFICATION='mandatory',
        ACCOUNT_EMAIL_REQUIRED=True,
        REST_SESSION_LOGIN=False,
        ACCOUNT_EMAIL_CONFIRMATION_HMAC=False,
    )
    def test_email_clash_with_existing_account(self):
        resp_body = {
            'id': '123123123123',
            'first_name': 'John',
            'gender': 'male',
            'last_name': 'Smith',
            'link': 'https://www.facebook.com/john.smith',
            'locale': 'en_US',
            'name': 'John Smith',
            'timezone': 2,
            'updated_time': '2014-08-13T10:14:38+0000',
            'username': 'john.smith',
            'verified': True,
            'email': self.EMAIL,
        }

        responses.add(
            responses.GET,
            self.graph_api_url,
            body=json.dumps(resp_body),
            status=200,
            content_type='application/json',
        )

        # test empty payload
        self.post(self.register_url, data={}, status_code=400)

        # register user and send email confirmation
        self.post(
            self.register_url,
            data=self.REGISTRATION_DATA,
            status_code=201,
        )
        new_user = get_user_model().objects.latest('id')
        self.assertEqual(new_user.username, self.REGISTRATION_DATA['username'])

        # verify email
        email_confirmation = new_user.emailaddress_set.get(email=self.EMAIL)\
            .emailconfirmation_set.order_by('-created')[0]
        self.post(
            self.verify_email_url,
            data={'key': email_confirmation.key},
            status_code=status.HTTP_200_OK,
        )

        self._login()
        self._logout()

        # fb log in with already existing email
        payload = {
            'access_token': 'abc123',
        }
        self.post(self.fb_login_url, data=payload, status_code=400)

    @responses.activate
    @override_settings(
        REST_USE_JWT=True,
    )
    def test_jwt(self):
        resp_body = '{"id":"123123123123","first_name":"John","gender":"male","last_name":"Smith","link":"https:\\/\\/www.facebook.com\\/john.smith","locale":"en_US","name":"John Smith","timezone":2,"updated_time":"2014-08-13T10:14:38+0000","username":"john.smith","verified":true}'  # noqa
        responses.add(
            responses.GET,
            self.graph_api_url,
            body=resp_body,
            status=200,
            content_type='application/json',
        )

        users_count = get_user_model().objects.all().count()
        payload = {
            'access_token': 'abc123',
        }

        self.post(self.fb_login_url, data=payload, status_code=200)
        self.assertIn('access_token', self.response.json.keys())
        self.assertIn('user', self.response.json.keys())

        self.assertEqual(get_user_model().objects.all().count(), users_count + 1)


@override_settings(ROOT_URLCONF='tests.urls')
class TestSocialConnectAuth(TestsMixin, TestCase):

    USERNAME = 'person'
    PASS = 'person'
    EMAIL = 'person1@world.com'
    REGISTRATION_DATA = {
        'username': USERNAME,
        'password1': PASS,
        'password2': PASS,
        'email': EMAIL,
    }

    def setUp(self):
        self.init()

        facebook_social_app = SocialApp.objects.create(
            provider='facebook',
            name='Facebook',
            client_id='123123123',
            secret='321321321',
        )

        twitter_social_app = SocialApp.objects.create(
            provider='twitter',
            name='Twitter',
            client_id='11223344',
            secret='55667788',
        )

        site = Site.objects.get_current()
        facebook_social_app.sites.add(site)
        twitter_social_app.sites.add(site)
        self.graph_api_url = GRAPH_API_URL + '/me'
        self.twitter_url = 'https://api.twitter.com/1.1/account/verify_credentials.json'

    @responses.activate
    def test_social_connect_no_auth(self):
        responses.add(
            responses.GET,
            self.graph_api_url,
            body='',
            status=200,
            content_type='application/json',
        )

        payload = {
            'access_token': 'abc123',
        }
        self.post(self.fb_connect_url, data=payload, status_code=403)
        self.post(self.tw_connect_url, data=payload, status_code=403)

    @responses.activate
    def test_social_connect(self):
        # register user
        self.post(
            self.register_url,
            data=self.REGISTRATION_DATA,
            status_code=201,
        )

        # Test Facebook
        resp_body = {
            'id': '123123123123',
            'first_name': 'John',
            'gender': 'male',
            'last_name': 'Smith',
            'link': 'https://www.facebook.com/john.smith',
            'locale': 'en_US',
            'name': 'John Smith',
            'timezone': 2,
            'updated_time': '2014-08-13T10:14:38+0000',
            'username': 'john.smith',
            'verified': True,
        }

        responses.add(
            responses.GET,
            self.graph_api_url,
            body=json.dumps(resp_body),
            status=200,
            content_type='application/json',
        )

        payload = {
            'access_token': 'abc123',
        }
        self.post(self.fb_connect_url, data=payload, status_code=200)
        self.assertIn('key', self.response.json.keys())

        # Test Twitter
        resp_body = {
            'id': '123123123123',
        }

        responses.add(
            responses.GET,
            self.twitter_url,
            body=json.dumps(resp_body),
            status=200,
            content_type='application/json',
        )

        payload = {
            'access_token': 'abc123',
            'token_secret': '1111222233334444',
        }

        self.post(self.tw_connect_url, data=payload)

        self.assertIn('key', self.response.json.keys())

        # Check current social accounts
        self.get(self.social_account_list_url)
        self.assertEqual(len(self.response.json), 2)
        self.assertEqual(self.response.json[0]['provider'], 'facebook')
        self.assertEqual(self.response.json[1]['provider'], 'twitter')

        facebook_social_account_id = self.response.json[0]['id']

        # Try disconnecting accounts
        self.incorrect_disconnect_url = reverse(
            'social_account_disconnect', args=[999999999],
        )
        self.post(self.incorrect_disconnect_url, status_code=404)

        self.disconnect_url = reverse(
            'social_account_disconnect', args=[facebook_social_account_id],
        )
        self.post(self.disconnect_url, status_code=200)

        # Check social accounts after disconnecting
        self.get(self.social_account_list_url)
        self.assertEqual(len(self.response.json), 1)
        self.assertEqual(self.response.json[0]['provider'], 'twitter')
