import json

from django.test import TestCase
from django.contrib.auth import get_user_model
from django.test.utils import override_settings
from django.contrib.sites.models import Site

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

from allauth.socialaccount.models import SocialApp
from allauth.socialaccount.providers.facebook.provider import GRAPH_API_URL
import responses

from rest_framework import status

from .mixins import TestsMixin


@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('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')
