from django.core import mail
from django.test import override_settings
from saas_base.models import UserEmail, Member
from tests.client import FixturesTestCase


class TestSignUpWithoutCreateUser(FixturesTestCase):
    user_id = FixturesTestCase.ADMIN_USER_ID

    def test_signup_success(self):
        data1 = {'username': 'demo', 'email': 'hi@foo.com', 'password': 'hello world'}
        resp = self.client.post('/m/auth/signup/request/', data=data1)
        self.assertEqual(resp.status_code, 204)
        self.assertEqual(len(mail.outbox), 1)
        data2 = {**data1, 'code': self.get_mail_auth_code()}
        resp = self.client.post('/m/auth/signup/confirm/', data=data2)
        self.assertEqual(resp.status_code, 200)

    def test_signup_existed_email(self):
        user = self.get_user()
        UserEmail.objects.create(user=user, email='hi@foo.com', primary=True, verified=True)
        data = {'username': 'foo', 'email': 'hi@foo.com', 'password': 'hello world'}
        resp = self.client.post('/m/auth/signup/request/', data=data)
        self.assertEqual(resp.status_code, 400)
        self.assertIn('existing', resp.json()['email'][0])

    def test_signup_blocked_email(self):
        rules = [{'backend': 'saas_base.rules.BlockedEmailDomains'}]
        with override_settings(SAAS={'SIGNUP_SECURITY_RULES': rules}):
            data = {'username': 'bar', 'email': 'hi@boofx.com', 'password': 'hello world'}
            resp = self.client.post('/m/auth/signup/request/', data=data)
            self.assertEqual(resp.status_code, 400)

        rules = [{'backend': 'saas_base.rules.BlockedEmailDomains', 'options': {'domains': ['bar.com']}}]
        with override_settings(SAAS={'SIGNUP_SECURITY_RULES': rules}):
            data = {'username': 'bar', 'email': 'hi@bar.com', 'password': 'hello world'}
            resp = self.client.post('/m/auth/signup/request/', data=data)
            self.assertEqual(resp.status_code, 400)

    def test_signup_too_many_dots(self):
        rules = [{'backend': 'saas_base.rules.AvoidTooManyDots'}]
        with override_settings(SAAS={'SIGNUP_SECURITY_RULES': rules}):
            data = {'username': 'bar', 'email': 'a.b.c.d.e.f@bar.com', 'password': 'hello world'}
            resp = self.client.post('/m/auth/signup/request/', data=data)
            self.assertEqual(resp.status_code, 400)

    def test_signup_using_plus(self):
        rules = [{'backend': 'saas_base.rules.AvoidUsingPlus'}]
        with override_settings(SAAS={'SIGNUP_SECURITY_RULES': rules}):
            data = {'username': 'bar', 'email': 'username+demo@gmail.com', 'password': 'hello world'}
            resp = self.client.post('/m/auth/signup/request/', data=data)
            self.assertEqual(resp.status_code, 400)

    def test_signup_turnstile(self):
        rules = [{'backend': 'saas_base.rules.Turnstile'}]
        with override_settings(SAAS={'SIGNUP_SECURITY_RULES': rules}):
            data = {'username': 'bar', 'email': 'hi@bar.com', 'password': 'hello world'}
            resp = self.client.post('/m/auth/signup/request/', data=data)
            self.assertEqual(resp.status_code, 400)

            data = {**data, 'cf-turnstile-response': '**token**'}
            with self.mock_requests('turnstile_success.json'):
                resp = self.client.post('/m/auth/signup/request/', data=data)
                self.assertEqual(resp.status_code, 204)

            data = {**data, 'cf-turnstile-response': '**token**'}
            with self.mock_requests('turnstile_failed.json'):
                resp = self.client.post('/m/auth/signup/request/', data=data)
                self.assertEqual(resp.status_code, 400)

    def test_turnstile_with_real_http(self):
        rules = [{'backend': 'saas_base.rules.Turnstile', 'options': {'secret': 'secret:turnstile'}}]
        with override_settings(SAAS={'SIGNUP_SECURITY_RULES': rules}):
            data = {
                'username': 'bar',
                'email': 'hi@bar.com',
                'password': 'hello world',
                'cf-turnstile-response': '**token**',
            }
            resp = self.client.post('/m/auth/signup/request/', data=data)
            self.assertEqual(resp.status_code, 204)

    def test_signup_with_membership_invite(self):
        # prepare membership
        email = 'hi@foo.com'
        Member.objects.create(tenant_id=self.tenant_id, email=email)

        data1 = {'username': 'demo', 'email': email, 'password': 'hello world'}
        self.client.post('/m/auth/signup/request/', data=data1)
        data2 = {**data1, 'code': self.get_mail_auth_code()}
        resp = self.client.post('/m/auth/signup/confirm/', data=data2)
        self.assertEqual(resp.status_code, 200)
        obj = UserEmail.objects.get(email=email)
        member = Member.objects.get(user_id=obj.user_id, tenant_id=self.tenant_id)
        self.assertEqual(member.status, Member.InviteStatus.WAITING)

    def test_signup_via_member_invite(self):
        email = 'hi@foo.com'
        obj = Member.objects.create(tenant_id=self.tenant_id, email=email)
        data = {'username': 'demo', 'password': 'hello world'}
        resp = self.client.post(f'/m/auth/signup/via/{obj.pk}/', data=data)
        self.assertEqual(resp.status_code, 200)

    def test_signup_with_waiting_member(self):
        user = self.get_user()
        obj = Member.objects.create(
            tenant_id=self.tenant_id,
            user=user,
            email=user.email,
            status=Member.InviteStatus.WAITING,
        )
        data = {'username': 'demo', 'password': 'hello world'}
        resp = self.client.post(f'/m/auth/signup/via/{obj.pk}/', data=data)
        self.assertEqual(resp.status_code, 404)


@override_settings(SAAS={'SIGNUP_REQUEST_CREATE_USER': True})
class TestSignUpWithCreateUser(FixturesTestCase):
    def test_signup_success(self):
        data1 = {'username': 'demo', 'email': 'hi@foo.com', 'password': 'hello world'}
        resp = self.client.post('/m/auth/signup/request/', data=data1)
        self.assertEqual(resp.status_code, 204)
        obj = UserEmail.objects.get(email='hi@foo.com')
        self.assertEqual(obj.verified, False)

        data2 = {'code': self.get_mail_auth_code(), 'email': 'hi@foo.com'}
        resp = self.client.post('/m/auth/signup/confirm/', data=data2)
        self.assertEqual(resp.status_code, 200)
        obj = UserEmail.objects.get(email='hi@foo.com')
        self.assertEqual(obj.verified, True)


class TestLoginAPI(FixturesTestCase):
    user_id = FixturesTestCase.OWNER_USER_ID

    def test_login_with_username(self):
        user = self.get_user()

        data = {'username': user.username, 'password': 'hello world'}
        resp = self.client.post('/m/auth/login/', data=data)
        self.assertEqual(resp.status_code, 400)

        user.set_password('hello world')
        user.save()

        resp = self.client.post('/m/auth/login/', data=data)
        self.assertEqual(resp.status_code, 200)
        self.assertIn('next', resp.json())

    def test_login_with_email(self):
        user = self.get_user()

        data = {'username': 'hi@foo.com', 'password': 'hello world'}
        resp = self.client.post('/m/auth/login/', data=data)
        self.assertEqual(resp.status_code, 400)

        user.set_password('hello world')
        user.save()

        obj = UserEmail.objects.create(user=user, email='hi@foo.com')
        resp = self.client.post('/m/auth/login/', data=data)
        self.assertEqual(resp.status_code, 400)

        obj.primary = True
        obj.verified = True
        obj.save()

        resp = self.client.post('/m/auth/login/', data=data)
        self.assertEqual(resp.status_code, 200)

        resp = self.client.get('/m/user/')
        self.assertEqual(resp.status_code, 200)
        data = resp.json()
        self.assertEqual(data['username'], user.username)


class TestInvitationAPI(FixturesTestCase):
    user_id = FixturesTestCase.ADMIN_USER_ID

    def test_request_status_invitation(self):
        email = 'hi@foo.com'
        obj = Member.objects.create(tenant_id=self.tenant_id, email=email)
        resp = self.client.get(f'/m/auth/invitation/{obj.pk}/')
        self.assertEqual(resp.status_code, 200)
        result = resp.json()
        self.assertEqual(result['status'], 'request')

    def test_waiting_status_invitation(self):
        user = self.get_user(self.EMPTY_USER_ID)
        obj = Member.objects.create(
            tenant_id=self.tenant_id,
            inviter_id=self.user_id,
            user=user,
            email=user.email,
            status=Member.InviteStatus.WAITING,
        )
        resp = self.client.get(f'/m/auth/invitation/{obj.pk}/')
        self.assertEqual(resp.status_code, 200)
        result = resp.json()
        self.assertEqual(result['status'], 'waiting')

    def test_active_status_invitation(self):
        user = self.get_user(self.EMPTY_USER_ID)
        obj = Member.objects.create(
            tenant_id=self.tenant_id,
            inviter_id=self.user_id,
            user=user,
            email=user.email,
            status=Member.InviteStatus.ACTIVE,
        )
        resp = self.client.get(f'/m/auth/invitation/{obj.pk}/')
        self.assertEqual(resp.status_code, 404)
