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

from epdb.models import MLRelativeReasoning, EnviFormer, Package


class Command(BaseCommand):
    """This command can be run with
    `python manage.py create_ml_models [model_names] -d [data_packages] OPTIONAL: -e [eval_packages]`
    For example, to train both EnviFormer and MLRelativeReasoning on BBD and SOIL and evaluate them on SLUDGE
    the below command would be used:
    `python manage.py create_ml_models enviformer mlrr -d bbd soil -e sludge
    """

    def add_arguments(self, parser):
        parser.add_argument(
            "model_names",
            nargs="+",
            type=str,
            help="The names of models to train. Options are: enviformer, mlrr",
        )
        parser.add_argument(
            "-d", "--data-packages", nargs="+", type=str, help="Packages for training"
        )
        parser.add_argument(
            "-e", "--eval-packages", nargs="*", type=str, help="Packages for evaluation", default=[]
        )
        parser.add_argument(
            "-r",
            "--rule-packages",
            nargs="*",
            type=str,
            help="Rule Packages mandatory for MLRR",
            default=[],
        )

    @transaction.atomic
    def handle(self, *args, **options):
        # Find Public Prediction Models package to add new models to
        try:
            pack = Package.objects.filter(name="Public Prediction Models")[0]
            bbd = Package.objects.filter(name="EAWAG-BBD")[0]
            soil = Package.objects.filter(name="EAWAG-SOIL")[0]
            sludge = Package.objects.filter(name="EAWAG-SLUDGE")[0]
            sediment = Package.objects.filter(name="EAWAG-SEDIMENT")[0]
        except IndexError:
            raise IndexError(
                "Can't find correct packages. They should be created with the bootstrap command"
            )

        def decode_packages(package_list):
            """Decode package strings into their respective packages"""
            packages = []
            for p in package_list:
                p = p.lower()
                if p == "bbd":
                    packages.append(bbd)
                elif p == "soil":
                    packages.append(soil)
                elif p == "sludge":
                    packages.append(sludge)
                elif p == "sediment":
                    packages.append(sediment)
                else:
                    raise ValueError(f"Unknown package {p}")
            return packages

        # Iteratively create models in options["model_names"]
        print(f"Creating models: {options['model_names']}")
        data_packages = decode_packages(options["data_packages"])
        eval_packages = decode_packages(options["eval_packages"])
        rule_packages = decode_packages(options["rule_packages"])
        for model_name in options["model_names"]:
            model_name = model_name.lower()
            if model_name == "enviformer" and s.ENVIFORMER_PRESENT:
                model = EnviFormer.create(
                    pack,
                    data_packages=data_packages,
                    eval_packages=eval_packages,
                    threshold=0.5,
                    name="EnviFormer - T0.5",
                    description="EnviFormer transformer",
                )
            elif model_name == "mlrr":
                model = MLRelativeReasoning.create(
                    package=pack,
                    rule_packages=rule_packages,
                    data_packages=data_packages,
                    eval_packages=eval_packages,
                    threshold=0.5,
                    name="ECC - BBD - T0.5",
                    description="ML Relative Reasoning",
                )
            else:
                raise ValueError(f"Cannot create model of type {model_name}, unknown model type")
            # Build the dataset for the model, train it, evaluate it and save it
            print(f"Building dataset for {model_name}")
            model.build_dataset()
            print(f"Training {model_name}")
            model.build_model()
            print(f"Evaluating {model_name}")
            model.evaluate_model()
            print(f"Saving {model_name}")
            model.save()
