import json

from django.conf import settings as s
from django.core.management.base import BaseCommand
from django.db import transaction

from epdb.logic import UserManager, GroupManager, PackageManager, SettingManager
from epdb.models import (
    UserSettingPermission,
    MLRelativeReasoning,
    Permission,
    User,
    ExternalDatabase,
    License,
)


class Command(BaseCommand):
    def add_arguments(self, parser):
        parser.add_argument(
            "-ol", "--only-licenses", action="store_true", help="Only create licenses."
        )

    def create_users(self):
        # Anonymous User
        if not User.objects.filter(email="anon@envipath.com").exists():
            anon = UserManager.create_user(
                "anonymous",
                "anon@envipath.com",
                "SuperSafe",
                is_active=True,
                add_to_group=False,
                set_setting=False,
            )
        else:
            anon = User.objects.get(email="anon@envipath.com")

        # Admin User
        if not User.objects.filter(email="admin@envipath.com").exists():
            admin = UserManager.create_user(
                "admin",
                "admin@envipath.com",
                "SuperSafe",
                is_active=True,
                add_to_group=False,
                set_setting=False,
            )
            admin.is_staff = True
            admin.is_superuser = True
            admin.save()
        else:
            admin = User.objects.get(email="admin@envipath.com")

        # System Group
        g = GroupManager.create_group(admin, "enviPath Users", "All enviPath Users")
        g.public = True
        g.save()

        g.user_member.add(anon)
        g.save()

        anon.default_group = g
        anon.save()

        admin.default_group = g
        admin.save()

        if not User.objects.filter(email="user0@envipath.com").exists():
            user0 = UserManager.create_user(
                "user0",
                "user0@envipath.com",
                "SuperSafe",
                is_active=True,
                add_to_group=False,
                set_setting=False,
            )
            user0.is_staff = True
            user0.is_superuser = True
            user0.save()
        else:
            user0 = User.objects.get(email="user0@envipath.com")

        g.user_member.add(user0)
        g.save()

        user0.default_group = g
        user0.save()

        return anon, admin, g, user0

    def create_licenses(self):
        """Create the six default licenses supported by enviPath"""
        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 import_package(self, data, owner, all_envipath_user_group):
        p = PackageManager.import_legacy_package(
            data, owner, keep_ids=True, add_import_timestamp=False, trust_reviewed=True
        )

        PackageManager.grant_read(owner, p, all_envipath_user_group)

        return p

    def create_default_setting(self, owner, packages):
        s = SettingManager.create_setting(
            owner,
            name="Global Default Setting",
            description="Global Default Setting containing BBD Rules and Max 30 Nodes and Max Depth of 8",
            max_nodes=30,
            max_depth=5,
            rule_packages=packages,
            model=None,
            model_threshold=None,
        )

        return s

    def populate_common_external_databases(self):
        """
        Helper function to populate common external databases.
        This can be called from a Django management command.
        """
        databases = [
            {
                "name": "PubChem Compound",
                "full_name": "PubChem Compound Database",
                "description": "Chemical database of small organic molecules",
                "base_url": "https://pubchem.ncbi.nlm.nih.gov",
                "url_pattern": "https://pubchem.ncbi.nlm.nih.gov/compound/{id}",
            },
            {
                "name": "PubChem Substance",
                "full_name": "PubChem Substance Database",
                "description": "Database of chemical substances",
                "base_url": "https://pubchem.ncbi.nlm.nih.gov",
                "url_pattern": "https://pubchem.ncbi.nlm.nih.gov/substance/{id}",
            },
            {
                "name": "ChEBI",
                "full_name": "Chemical Entities of Biological Interest",
                "description": "Dictionary of molecular entities",
                "base_url": "https://www.ebi.ac.uk/chebi",
                "url_pattern": "https://www.ebi.ac.uk/chebi/searchId.do?chebiId=CHEBI:{id}",
            },
            {
                "name": "RHEA",
                "full_name": "RHEA Reaction Database",
                "description": "Comprehensive resource of biochemical reactions",
                "base_url": "https://www.rhea-db.org",
                "url_pattern": "https://www.rhea-db.org/rhea/{id}",
            },
            {
                "name": "KEGG Reaction",
                "full_name": "KEGG Reaction Database",
                "description": "Database of biochemical reactions",
                "base_url": "https://www.genome.jp",
                "url_pattern": "https://www.genome.jp/entry/{id}",
            },
            {
                "name": "UniProt",
                "full_name": "MetaCyc Metabolic Pathway Database",
                "description": "UniProt is a freely accessible database of protein sequence and functional information",
                "base_url": "https://www.uniprot.org",
                "url_pattern": 'https://www.uniprot.org/uniprotkb?query="{id}"',
            },
        ]

        for db_info in databases:
            ExternalDatabase.objects.get_or_create(name=db_info["name"], defaults=db_info)

    @transaction.atomic
    def handle(self, *args, **options):
        # Create licenses
        self.create_licenses()
        if options.get("only_licenses", False):
            return
        # Create users
        anon, admin, g, user0 = self.create_users()

        self.populate_common_external_databases()

        # Import Packages
        packages = [
            "EAWAG-BBD.json",
            "EAWAG-SOIL.json",
            "EAWAG-SLUDGE.json",
            "EAWAG-SEDIMENT.json",
        ]

        mapping = {}
        for p in packages:
            print(f"Importing {p}...")
            package_data = json.loads(
                open(
                    s.BASE_DIR / "fixtures" / "packages" / "2025-07-18" / p, encoding="utf-8"
                ).read()
            )
            imported_package = self.import_package(package_data, admin, g)
            mapping[p.replace(".json", "")] = imported_package

        setting = self.create_default_setting(admin, [mapping["EAWAG-BBD"]])
        setting.public = True
        setting.save()
        setting.make_global_default()

        for u in [anon, user0]:
            u.default_setting = setting
            u.save()

            usp = UserSettingPermission()
            usp.user = u
            usp.setting = setting
            usp.permission = Permission.READ[0]
            usp.save()

        # Create Model Package
        pack = PackageManager.create_package(
            admin,
            "Public Prediction Models",
            "Package to make Prediction Models publicly available",
        )
        pack.reviewed = True
        pack.save()

        # Create RR
        ml_model = MLRelativeReasoning.create(
            package=pack,
            rule_packages=[mapping["EAWAG-BBD"]],
            data_packages=[mapping["EAWAG-BBD"]],
            threshold=0.5,
            name="ECC - BBD - T0.5",
            description="ML Relative Reasoning",
        )

        ml_model.build_dataset()
        ml_model.build_model()
