"""Test cases for the limits extension."""

from testfixtures import compare

from django.urls import reverse

from rest_framework.authtoken.models import Token

from modoboa.admin.factories import populate_database
from modoboa.admin.models import Domain
from modoboa.core import factories as core_factories
from modoboa.core.models import User
from modoboa.lib import permissions, tests as lib_tests
from ... import utils


class APIAdminLimitsTestCase(lib_tests.ModoAPITestCase):
    """Check that limits are used also by the API."""

    @classmethod
    def setUpTestData(cls):  # NOQA:N802
        """Create test data."""
        super().setUpTestData()
        for name, _definition in utils.get_user_limit_templates():
            cls.localconfig.parameters.set_value(f"deflt_user_{name}_limit", 2)
        cls.localconfig.save()
        populate_database()
        cls.user = User.objects.get(username="admin@test.com")
        cls.da_token = Token.objects.create(user=cls.user)
        cls.reseller = core_factories.UserFactory(
            username="reseller",
            groups=("Resellers",),
        )
        cls.r_token = Token.objects.create(user=cls.reseller)

    def test_domadmins_limit(self):
        """Check domain admins limit."""
        self.client.credentials(HTTP_AUTHORIZATION="Token " + self.r_token.key)

        limit = self.reseller.userobjectlimit_set.get(name="domain_admins")
        url = reverse("v1:account-list")
        data = {
            "username": "fromapi@test.com",
            "role": "DomainAdmins",
            "password": "Toto1234",
        }
        response = self.client.post(url, data, format="json")
        self.assertEqual(response.status_code, 201)
        self.assertFalse(limit.is_exceeded())

        data["username"] = "fromapi2@test.com"
        response = self.client.post(url, data, format="json")
        self.assertEqual(response.status_code, 201)
        self.assertTrue(limit.is_exceeded())

        data["username"] = "fromapi3@test.com"
        response = self.client.post(url, data, format="json")
        self.assertEqual(response.status_code, 400)

        user = User.objects.get(username="user@test.com")
        domain = Domain.objects.get(name="test.com")
        domain.add_admin(self.reseller)
        url = reverse("v1:account-detail", args=[user.pk])
        data = {
            "username": user.username,
            "role": "DomainAdmins",
            "password": "Toto1234",
            "mailbox": {"full_address": user.username, "quota": user.mailbox.quota},
        }
        response = self.client.put(url, data, format="json")
        self.assertEqual(response.status_code, 400)

    def test_domains_limit(self):
        """Check domains limit."""
        self.client.credentials(HTTP_AUTHORIZATION="Token " + self.r_token.key)
        limit = self.reseller.userobjectlimit_set.get(name="domains")
        quota = self.reseller.userobjectlimit_set.get(name="quota")
        quota.max_value = 3
        quota.save(update_fields=["max_value"])
        url = reverse("v1:domain-list")
        data = {"name": "test3.com", "quota": 1}
        response = self.client.post(url, data, format="json")
        self.assertEqual(response.status_code, 201)
        self.assertFalse(limit.is_exceeded())
        self.assertFalse(quota.is_exceeded())

        data["name"] = "test4.com"
        response = self.client.post(url, data, format="json")
        self.assertEqual(response.status_code, 201)
        self.assertTrue(limit.is_exceeded())
        self.assertFalse(quota.is_exceeded())

        data["name"] = "test5.com"
        response = self.client.post(url, data, format="json")
        self.assertEqual(response.status_code, 403)
        self.assertEqual(response.json()["error"], "Domains: limit reached")

        resp = self.client.delete(
            reverse("v1:domain-detail", args=[Domain.objects.get(name="test4.com").pk])
        )
        self.assertEqual(resp.status_code, 204)
        data["quota"] = 0
        response = self.client.post(url, data, format="json")
        self.assertEqual(response.status_code, 400)
        self.assertEqual(
            response.json()["error"],
            "You're not allowed to define unlimited values",
        )

    def test_domain_aliases_limit(self):
        """Check domain aliases limit."""
        self.client.credentials(HTTP_AUTHORIZATION="Token " + self.r_token.key)
        domain = Domain.objects.get(name="test.com")
        domain.add_admin(self.reseller)
        limit = self.reseller.userobjectlimit_set.get(name="domain_aliases")
        url = reverse("v1:domain_alias-list")
        data = {"name": "dalias1.com", "target": domain.pk}
        response = self.client.post(url, data, format="json")
        self.assertEqual(response.status_code, 201)
        self.assertFalse(limit.is_exceeded())

        data["name"] = "dalias2.com"
        response = self.client.post(url, data, format="json")
        self.assertEqual(response.status_code, 201)
        self.assertTrue(limit.is_exceeded())

        data["username"] = "dalias3.com"
        response = self.client.post(url, data, format="json")
        self.assertEqual(response.status_code, 400)

    def test_mailboxes_limit(self):
        """Check mailboxes limit."""
        self.client.credentials(HTTP_AUTHORIZATION="Token " + self.da_token.key)

        limit = self.user.userobjectlimit_set.get(name="mailboxes")
        url = reverse("v1:account-list")
        data = {
            "username": "fromapi@test.com",
            "role": "SimpleUsers",
            "password": "Toto1234",
            "mailbox": {"full_address": "fromapi@test.com", "quota": 10},
        }
        response = self.client.post(url, data, format="json")
        self.assertEqual(response.status_code, 201)
        self.assertFalse(limit.is_exceeded())

        data["username"] = "fromapi2@test.com"
        data["mailbox"]["full_address"] = "fromapi2@test.com"
        response = self.client.post(url, data, format="json")
        self.assertEqual(response.status_code, 201)
        self.assertTrue(limit.is_exceeded())

        data["username"] = "fromapi3@test.com"
        data["mailbox"]["full_address"] = "fromapi3@test.com"
        response = self.client.post(url, data, format="json")
        self.assertEqual(response.status_code, 400)

    def test_aliases_limit(self):
        """Check mailbox aliases limit."""
        self.client.credentials(HTTP_AUTHORIZATION="Token " + self.da_token.key)

        limit = self.user.userobjectlimit_set.get(name="mailbox_aliases")
        url = reverse("v1:alias-list")
        data = {
            "address": "alias_fromapi@test.com",
            "recipients": [
                "user@test.com",
                "postmaster@test.com",
                "user_éé@nonlocal.com",
            ],
        }
        response = self.client.post(url, data, format="json")
        self.assertEqual(response.status_code, 201)
        self.assertFalse(limit.is_exceeded())

        data["address"] = "alias_fromapi2@test.com"
        response = self.client.post(url, data, format="json")
        self.assertEqual(response.status_code, 201)
        self.assertTrue(limit.is_exceeded())

        data["address"] = "alias_fromapi3@test.com"
        response = self.client.post(url, data, format="json")
        self.assertEqual(response.status_code, 400)


class APIDomainLimitsTestCase(lib_tests.ModoAPITestCase):
    """Check that limits are used also by the API."""

    @classmethod
    def setUpTestData(cls):  # NOQA:N802
        """Create test data."""
        super().setUpTestData()
        cls.localconfig.parameters.set_value("enable_domain_limits", True)
        for name, _definition in utils.get_domain_limit_templates():
            cls.localconfig.parameters.set_value(f"deflt_domain_{name}_limit", 2)
        cls.localconfig.save()
        populate_database()

    def test_mailboxes_limit(self):
        """Check mailboxes limit."""
        domain = Domain.objects.get(name="test.com")
        limit = domain.domainobjectlimit_set.get(name="mailboxes")
        self.assertTrue(limit.is_exceeded())
        url = reverse("v1:account-list")
        data = {
            "username": "fromapi@test.com",
            "role": "SimpleUsers",
            "password": "Toto1234",
            "mailbox": {"full_address": "fromapi@test.com", "quota": 10},
        }
        response = self.client.post(url, data, format="json")
        self.assertEqual(response.status_code, 400)

    def test_domain_aliases_limit(self):
        """Check domain_aliases limit."""
        domain = Domain.objects.get(name="test.com")
        limit = domain.domainobjectlimit_set.get(name="domain_aliases")
        url = reverse("v1:domain_alias-list")
        data = {"name": "dalias1.com", "target": domain.pk}
        response = self.client.post(url, data, format="json")
        self.assertEqual(response.status_code, 201)
        data["name"] = "dalias2.com"
        response = self.client.post(url, data, format="json")
        self.assertEqual(response.status_code, 201)
        self.assertTrue(limit.is_exceeded())
        data["name"] = "dalias3.com"
        response = self.client.post(url, data, format="json")
        self.assertEqual(response.status_code, 400)

    def test_mailbox_aliases_limit(self):
        """Check mailbox_aliases limit."""
        domain = Domain.objects.get(name="test.com")
        limit = domain.domainobjectlimit_set.get(name="mailbox_aliases")
        self.assertTrue(limit.is_exceeded())
        url = reverse("v1:alias-list")
        data = {
            "address": "alias_fromapi@test.com",
            "recipients": [
                "user@test.com",
                "postmaster@test.com",
                "user_éé@nonlocal.com",
            ],
        }
        response = self.client.post(url, data, format="json")
        self.assertEqual(response.status_code, 400)


class ResourcesAPITestCase(lib_tests.ModoAPITestCase):
    """Check resources API."""

    @classmethod
    def setUpTestData(cls):  # NOQA:N802
        """Create test data."""
        super().setUpTestData()
        for name, _definition in utils.get_user_limit_templates():
            cls.localconfig.parameters.set_value(f"deflt_user_{name}_limit", 2)
        cls.localconfig.save()
        populate_database()
        cls.user = User.objects.get(username="admin@test.com")
        cls.da_token = Token.objects.create(user=cls.user)
        cls.reseller = core_factories.UserFactory(
            username="reseller",
            groups=("Resellers",),
        )
        cls.r_token = Token.objects.create(user=cls.reseller)

    def test_get_admin_resources(self):
        """Retrieve admin resources."""
        url = reverse("v1:resources-detail", args=[self.user.pk])
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)
        expected = {
            "quota": 2,
            "mailboxes": 2,
            "domain_admins": 2,
            "domain_aliases": 2,
            "domains": 2,
            "mailbox_aliases": 2,
        }
        compare(expected, response.data)

        # As reseller => fails
        self.client.credentials(HTTP_AUTHORIZATION=f"Token {self.r_token.key}")
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

        # As domain admin => fails
        self.client.credentials(HTTP_AUTHORIZATION=f"Token {self.da_token.key}")
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

    def test_update_resources(self):
        """Update resources."""
        url = reverse("v1:resources-detail", args=[self.reseller.pk])
        response = self.client.get(url)
        resources = response.data
        resources.update({"domains": 1000, "mailboxes": 1000})
        response = self.client.put(url, resources)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(
            self.reseller.userobjectlimit_set.get(name="domains").max_value, 1000
        )

        # As domain admin => fails
        self.client.credentials(HTTP_AUTHORIZATION=f"Token {self.da_token.key}")
        resources.update({"domains": 2, "mailboxes": 2})
        url = reverse("v1:resources-detail", args=[self.user.pk])
        response = self.client.put(url, resources)
        self.assertEqual(response.status_code, 404)

        # As reseller => ok
        permissions.grant_access_to_object(self.reseller, self.user, True)
        self.client.credentials(HTTP_AUTHORIZATION=f"Token {self.r_token.key}")
        resources.update({"domains": 500, "mailboxes": 500})
        url = reverse("v1:resources-detail", args=[self.user.pk])
        response = self.client.put(url, resources)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(
            self.user.userobjectlimit_set.get(name="domains").max_value, 500
        )
        self.assertEqual(
            self.reseller.userobjectlimit_set.get(name="domains").max_value, 502
        )
        resources.update({"domains": 1003})
        response = self.client.put(url, resources)
        self.assertEqual(response.status_code, 424)
