from unittest.mock import Mock
from django.utils import timezone
from django.test import RequestFactory
from rest_framework.request import Request
from rest_framework.exceptions import PermissionDenied
from saas_base.endpoints.tenant import TenantItemEndpoint
from saas_base.drf.permissions import (
    IsTenantOwner,
    IsTenantOwnerOrReadOnly,
    IsTenantActive,
    IsTenantActiveOrReadOnly,
    HasResourcePermission,
)
from saas_base.models import Member, get_tenant_model
from tests.demo_app.models import TenantSecret
from tests.client import FixturesTestCase

Tenant = get_tenant_model()


class TestIsTenantOwner(FixturesTestCase):
    def setUp(self):
        super().setUp()
        self.permission = IsTenantOwner()
        self.view = Mock()
        factory = RequestFactory()
        self.request = Request(factory.get('/'))
        self.request.tenant_id = self.tenant_id

    def test_has_permission_owner(self):
        self.request.user = self.force_login(self.OWNER_USER_ID)
        self.request.tenant_id = self.tenant_id
        self.request.member = Member.objects.get(tenant=self.tenant, user_id=self.OWNER_USER_ID)
        self.assertTrue(self.permission.has_permission(self.request, self.view))

    def test_has_permission_member(self):
        self.request.user = self.force_login(self.MEMBER_USER_ID)
        self.assertFalse(self.permission.has_permission(self.request, self.view))

        # readonly passes
        permission = IsTenantOwnerOrReadOnly()
        self.assertTrue(permission.has_permission(self.request, self.view))

    def test_has_permission_member_with_owner_role(self):
        Member.objects.create(tenant=self.tenant, user_id=self.MEMBER_USER_ID, role='OWNER')
        user = self.force_login(self.MEMBER_USER_ID)
        self.request.user = user
        # Mock member attribute on request
        self.request.member = Member.objects.get(tenant=self.tenant, user=user)
        self.assertTrue(self.permission.has_permission(self.request, self.view))

    def test_has_object_permission(self):
        user = self.force_login(self.OWNER_USER_ID)
        self.request.user = user
        self.request.tenant_id = None
        self.view.object_tenant_id_field = 'tenant_id'
        obj = TenantSecret.objects.create(tenant_id=self.tenant_id)
        self.assertTrue(self.permission.has_object_permission(self.request, self.view, obj))


class TestIsTenantActive(FixturesTestCase):
    def setUp(self):
        super().setUp()
        self.permission = IsTenantActive()
        self.view = Mock()
        factory = RequestFactory()
        self.request = Request(factory.get('/'))
        self.request.tenant_id = self.tenant_id

    def test_active_tenant(self):
        user = self.force_login(self.MEMBER_USER_ID)
        self.request.user = user
        self.request.member = Member.objects.create(tenant=self.tenant, user_id=self.MEMBER_USER_ID)
        self.assertTrue(self.permission.has_permission(self.request, self.view))

    def test_expired_tenant(self):
        tenant = self.get_tenant(self.tenant_id)
        tenant.expires_at = timezone.now() - timezone.timedelta(days=1)
        tenant.save()
        user = self.force_login(self.MEMBER_USER_ID)
        self.request.user = user

        with self.assertRaises(PermissionDenied):
            self.permission.has_permission(self.request, self.view)

        # readonly passes
        permission = IsTenantActiveOrReadOnly()
        self.assertTrue(permission.has_permission(self.request, self.view))


class TestHasResourcePermission(FixturesTestCase):
    def setUp(self):
        super().setUp()
        self.permission = HasResourcePermission()
        self.view = TenantItemEndpoint.as_view()
        self.view.required_permission = 'org.info.view'
        factory = RequestFactory()
        self.request = Request(factory.get('/'))
        self.request._auth = None
        self.request.tenant_id = self.tenant_id

    def test_has_permission_owner(self):
        user = self.force_login(self.OWNER_USER_ID)
        self.request.user = user
        self.assertTrue(self.permission.has_permission(self.request, self.view))

    def test_has_permission_member_with_perm(self):
        user = self.force_login(self.MEMBER_USER_ID)
        member = Member.objects.create(tenant=self.tenant, user=user, role='MEMBER')
        self.request.user = user
        self.assertTrue(self.permission.has_permission(self.request, self.view))

        # Test without permission
        member.role = None
        member.save()
        self.assertFalse(self.permission.has_permission(self.request, self.view))

    def test_has_permission_with_invalid_token_scope(self):
        user = self.force_login(self.MEMBER_USER_ID)
        self.request.user = user

        class Token1:
            scope = 'profile'

        self.request._auth = Token1
        self.assertFalse(self.permission.has_permission(self.request, self.view))

        class Token2:
            scopes = ['profile']

        self.request._auth = Token2
        self.assertFalse(self.permission.has_permission(self.request, self.view))

        class Token3:
            def get_scopes(self):
                return ['profile']

        self.request._auth = Token3()
        self.assertFalse(self.permission.has_permission(self.request, self.view))

    def test_has_permission_with_valid_token_scope(self):
        user = self.force_login(self.MEMBER_USER_ID)
        self.request.user = user
        Member.objects.create(tenant=self.tenant, user=user, role='MEMBER')

        class Token1:
            scope = 'org:read'

        self.request._auth = Token1
        self.assertTrue(self.permission.has_permission(self.request, self.view))

        class Token2:
            scopes = ['org:read']

        self.request._auth = Token2
        self.assertTrue(self.permission.has_permission(self.request, self.view))
