import json

from django.test.client import RequestFactory
from django.test.utils import override_settings
from django.urls import reverse

from allauth.account import app_settings as account_settings
from allauth.account.models import EmailAddress
from allauth.socialaccount import providers
from allauth.socialaccount.models import SocialAccount
from allauth.socialaccount.tests import OAuth2TestsMixin
from allauth.tests import MockedResponse, TestCase, patch
from allauth.utils import get_user_model

from .provider import FacebookProvider


@override_settings(
    SOCIALACCOUNT_AUTO_SIGNUP=True,
    ACCOUNT_SIGNUP_FORM_CLASS=None,
    LOGIN_REDIRECT_URL="/accounts/profile/",
    ACCOUNT_EMAIL_VERIFICATION=account_settings.EmailVerificationMethod.NONE,
    SOCIALACCOUNT_PROVIDERS={"facebook": {"AUTH_PARAMS": {}, "VERIFIED_EMAIL": False}},
)
class FacebookTests(OAuth2TestsMixin, TestCase):
    provider_id = FacebookProvider.id

    facebook_data = """
        {
           "id": "630595557",
           "name": "Raymond Penners",
           "first_name": "Raymond",
           "last_name": "Penners",
           "email": "raymond.penners@example.com",
           "link": "https://www.facebook.com/raymond.penners",
           "username": "raymond.penners",
           "birthday": "07/17/1973",
           "work": [
              {
                 "employer": {
                    "id": "204953799537777",
                    "name": "IntenCT"
                 }
              }
           ],
           "timezone": 1,
           "locale": "nl_NL",
           "verified": true,
           "updated_time": "2012-11-30T20:40:33+0000"
        }"""

    def get_mocked_response(self, data=None):
        if data is None:
            data = self.facebook_data
        return MockedResponse(200, data)

    def test_username_conflict(self):
        User = get_user_model()
        User.objects.create(username="raymond.penners")
        self.login(self.get_mocked_response())
        socialaccount = SocialAccount.objects.get(uid="630595557")
        self.assertEqual(socialaccount.user.username, "raymond")

    def test_username_based_on_provider(self):
        self.login(self.get_mocked_response())
        socialaccount = SocialAccount.objects.get(uid="630595557")
        self.assertEqual(socialaccount.user.username, "raymond.penners")

    def test_username_based_on_provider_with_simple_name(self):
        data = '{"id": "1234567", "name": "Harvey McGillicuddy"}'
        self.login(self.get_mocked_response(data=data))
        socialaccount = SocialAccount.objects.get(uid="1234567")
        self.assertEqual(socialaccount.user.username, "harvey")

    def test_media_js(self):
        provider = providers.registry.by_id(FacebookProvider.id)
        request = RequestFactory().get(reverse("account_login"))
        request.session = {}
        script = provider.media_js(request)
        self.assertTrue('"appId": "app123id"' in script)

    def test_login_by_token(self):
        resp = self.client.get(reverse("account_login"))
        with patch(
            "allauth.socialaccount.providers.facebook.views" ".requests"
        ) as requests_mock:
            mocks = [self.get_mocked_response().json()]
            requests_mock.get.return_value.json = lambda: mocks.pop()
            resp = self.client.post(
                reverse("facebook_login_by_token"),
                data={"access_token": "dummy"},
            )
            self.assertRedirects(
                resp, "/accounts/profile/", fetch_redirect_response=False
            )

    @override_settings(
        SOCIALACCOUNT_PROVIDERS={
            "facebook": {
                "AUTH_PARAMS": {"auth_type": "reauthenticate"},
                "VERIFIED_EMAIL": False,
            }
        }
    )
    def test_login_by_token_reauthenticate(self):
        resp = self.client.get(reverse("account_login"))
        nonce = json.loads(resp.context["fb_data"])["loginOptions"]["auth_nonce"]
        with patch(
            "allauth.socialaccount.providers.facebook.views" ".requests"
        ) as requests_mock:
            mocks = [self.get_mocked_response().json(), {"auth_nonce": nonce}]
            requests_mock.get.return_value.json = lambda: mocks.pop()
            resp = self.client.post(
                reverse("facebook_login_by_token"),
                data={"access_token": "dummy"},
            )
            self.assertRedirects(
                resp, "/accounts/profile/", fetch_redirect_response=False
            )

    @override_settings(SOCIALACCOUNT_PROVIDERS={"facebook": {"VERIFIED_EMAIL": True}})
    def test_login_verified(self):
        emailaddress = self._login_verified()
        self.assertTrue(emailaddress.verified)

    def test_login_unverified(self):
        emailaddress = self._login_verified()
        self.assertFalse(emailaddress.verified)

    def _login_verified(self):
        self.login(self.get_mocked_response())
        return EmailAddress.objects.get(email="raymond.penners@example.com")
