from django.conf import settings as s
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase, override_settings, tag
from django.urls import reverse

from epdb.logic import UserManager
from epdb.models import (
    Group,
    GroupPackagePermission,
    License,
    Permission,
    UserPackagePermission,
)

Package = s.GET_PACKAGE_MODEL()


@override_settings(MODEL_DIR=s.FIXTURE_DIRS[0] / "models", CELERY_TASK_ALWAYS_EAGER=True)
class PackageViewTest(TestCase):
    fixtures = ["test_fixtures_incl_model.jsonl.gz"]

    @classmethod
    def setUpClass(cls):
        super(PackageViewTest, cls).setUpClass()
        cls.user1 = UserManager.create_user(
            "user1",
            "user1@envipath.com",
            "SuperSafe",
            set_setting=False,
            add_to_group=True,
            is_active=True,
        )
        cls.user2 = UserManager.create_user(
            "user2",
            "user2@envipath.com",
            "SuperSafe",
            set_setting=False,
            add_to_group=True,
            is_active=True,
        )
        # Create the default license set.
        cc_strings = ["by", "by-nc", "by-nc-nd", "by-nc-sa", "by-nd", "by-sa"]
        for cc_string in cc_strings:
            if not License.objects.filter(cc_string=cc_string).exists():
                new_license = License()
                new_license.cc_string = cc_string
                new_license.link = f"https://creativecommons.org/licenses/{cc_string}/4.0/"
                new_license.image_link = f"https://licensebuttons.net/l/{cc_string}/4.0/88x31.png"
                new_license.save()

    def setUp(self):
        self.client.force_login(self.user1)

    def test_create_package(self):
        response = self.client.post(
            reverse("packages"),
            {
                "package-name": "Test Package",
                "package-description": "Just a Description",
            },
        )
        self.assertEqual(response.status_code, 302)
        package_url = response.url

        p = Package.objects.get(url=package_url)

        self.assertEqual(p.name, "Test Package")
        self.assertEqual(p.description, "Just a Description")

        upp = UserPackagePermission.objects.get(package=p, user=self.user1)
        self.assertEqual(upp.permission, Permission.ALL[0])

    def test_import_package(self):
        file = SimpleUploadedFile(
            "EAWAG-BBD_32de3cf4-e3e6-4168-956e-32fa5ddb0ce1.json",
            open(
                s.FIXTURE_DIRS[0] / "EAWAG-BBD_32de3cf4-e3e6-4168-956e-32fa5ddb0ce1.json", "rb"
            ).read(),
            content_type="application/json",
        )

        response = self.client.post(
            reverse("packages"), {"file": file, "hidden": "import-package-json"}
        )

        self.assertEqual(response.status_code, 302)
        package_url = response.url

        p = Package.objects.get(url=package_url)

        self.assertEqual(p.pathways.count(), 219)
        self.assertEqual(p.rules.count(), 498)
        self.assertEqual(p.compounds.count(), 1399)
        self.assertEqual(p.reactions.count(), 1480)
        self.assertEqual(p.scenarios.count(), 1914)

        upp = UserPackagePermission.objects.get(package=p, user=self.user1)
        self.assertEqual(upp.permission, Permission.ALL[0])

    @tag("slow")
    def test_import_legacy_package(self):
        file = SimpleUploadedFile(
            "EAWAG-BBD.json",
            open(s.FIXTURE_DIRS[0] / "packages" / "2025-07-18" / "EAWAG-BBD.json", "rb").read(),
            content_type="application/json",
        )

        response = self.client.post(
            reverse("packages"), {"file": file, "hidden": "import-legacy-package-json"}
        )

        self.assertEqual(response.status_code, 302)
        package_url = response.url

        p = Package.objects.get(url=package_url)

        self.assertEqual(p.pathways.count(), 219)
        self.assertEqual(p.rules.count(), 498)
        self.assertEqual(p.compounds.count(), 1399)
        self.assertEqual(p.reactions.count(), 1480)
        self.assertEqual(p.scenarios.count(), 1914)

        upp = UserPackagePermission.objects.get(package=p, user=self.user1)
        self.assertEqual(upp.permission, Permission.ALL[0])

    def test_edit_package(self):
        response = self.client.post(
            reverse("packages"),
            {
                "package-name": "Test Package",
                "package-description": "Just a Description",
            },
        )
        self.assertEqual(response.status_code, 302)
        package_url = response.url

        self.client.post(
            package_url,
            {
                "package-name": "New Name",
                "package-description": "New Description",
            },
        )

        p = Package.objects.get(url=package_url)

        self.assertEqual(p.name, "New Name")
        self.assertEqual(p.description, "New Description")

    def test_edit_package_permissions(self):
        response = self.client.post(
            reverse("packages"),
            {
                "package-name": "Test Package",
                "package-description": "Just a Description",
            },
        )
        self.assertEqual(response.status_code, 302)
        package_url = response.url
        p = Package.objects.get(url=package_url)

        with self.assertRaises(UserPackagePermission.DoesNotExist):
            UserPackagePermission.objects.get(package=p, user=self.user2)

        self.client.post(
            package_url,
            {
                "grantee": self.user2.url,
                "read": "on",
                "write": "on",
            },
        )

        upp = UserPackagePermission.objects.get(package=p, user=self.user2)

        self.assertEqual(upp.permission, Permission.WRITE[0])

    def test_publish_package(self):
        response = self.client.post(
            reverse("packages"),
            {
                "package-name": "Test Package",
                "package-description": "Just a Description",
            },
        )
        self.assertEqual(response.status_code, 302)
        package_url = response.url
        p = Package.objects.get(url=package_url)

        self.client.post(package_url, {"hidden": "publish-package"})

        self.assertEqual(Group.objects.filter(public=True).count(), 1)
        g = Group.objects.get(public=True)
        gpp = GroupPackagePermission.objects.get(package=p, group=g)
        self.assertEqual(gpp.permission, Permission.READ[0])

    def test_set_package_license(self):
        response = self.client.post(
            reverse("packages"),
            {
                "package-name": "Test Package",
                "package-description": "Just a Description",
            },
        )

        package_url = response.url
        p = Package.objects.get(url=package_url)

        self.client.post(package_url, {"license": "no-license"})

        self.assertIsNone(p.license)

        # Test other possible licenses
        cc_strings = ["by", "by-nc", "by-nc-nd", "by-nc-sa", "by-nd", "by-sa"]
        for cc_string in cc_strings:
            self.client.post(package_url, {"license": cc_string})
            # Without this, the instance of p doesn't have the license. However, the one retrieved with get does
            p = Package.objects.get(url=package_url)
            self.assertEqual(
                p.license.link, f"https://creativecommons.org/licenses/{cc_string}/4.0/"
            )

        # Test again to ensure that Licenses are reused
        cc_strings = ["by", "by-nc", "by-nc-nd", "by-nc-sa", "by-nd", "by-sa"]
        for cc_string in cc_strings:
            self.client.post(package_url, {"license": cc_string})
            # Without this, the instance of p doesn't have the license. However, the one retrieved with get does
            p = Package.objects.get(url=package_url)
            self.assertEqual(
                p.license.link, f"https://creativecommons.org/licenses/{cc_string}/4.0/"
            )

        self.assertEqual(License.objects.count(), len(cc_strings))

    def test_delete_package(self):
        response = self.client.post(
            reverse("packages"),
            {
                "package-name": "Test Package",
                "package-description": "Just a Description",
            },
        )

        package_url = response.url
        p = Package.objects.get(url=package_url)

        p.delete()

        response = self.client.get(package_url)
        self.assertEqual(response.status_code, 404)

    def test_delete_default_package(self):
        self.client.force_login(self.user1)
        # Try to delete the default package
        response = self.client.post(self.user1.default_package.url, {"hidden": "delete"})

        self.assertEqual(response.status_code, 400)
        self.assertTrue(
            "You cannot delete the default package. "
            "If you want to delete this package you have to "
            "set another default package first" in response.content.decode()
        )
