#
# SPDX-License-Identifier: GPL-2.0-only
#

from django.core.management.base import BaseCommand, CommandError
from django.db import transaction

from django.core.management import call_command
from bldcontrol.bbcontroller import getBuildEnvironmentController, ShellCmdException
from bldcontrol.models import BuildRequest, BuildEnvironment, BRError
from orm.models import ToasterSetting, Build, Layer

import os
import traceback
import warnings


def DN(path):
    if path is None:
        return ""
    else:
        return os.path.dirname(path)


class Command(BaseCommand):
    args = ""
    help = "Verifies that the configured settings are valid and usable, or prompts the user to fix the settings."

    def __init__(self, *args, **kwargs):
        super(Command, self).__init__(*args, **kwargs)
        self.guesspath = DN(DN(DN(DN(DN(DN(DN(__file__)))))))

    def _verify_build_environment(self):
        # provide a local build env. This will be extended later to include non local
        if BuildEnvironment.objects.count() == 0:
            BuildEnvironment.objects.create(betype=BuildEnvironment.TYPE_LOCAL)

        # we make sure we have builddir and sourcedir for all defined build envionments
        for be in BuildEnvironment.objects.all():
            be.needs_import = False
            def _verify_be():
                is_changed = False

                def _update_sourcedir():
                    be.sourcedir = os.environ.get('TOASTER_DIR')
                    return True

                if len(be.sourcedir) == 0:
                    is_changed = _update_sourcedir()

                if not be.sourcedir.startswith("/"):
                    print("\n -- Validation: The layers checkout directory must be set to an absolute path.")
                    is_changed = _update_sourcedir()

                if is_changed:
                    if be.betype == BuildEnvironment.TYPE_LOCAL:
                        be.needs_import = True
                    return True

                def _update_builddir():
                    be.builddir = os.environ.get('TOASTER_DIR')+"/build"
                    return True

                if len(be.builddir) == 0:
                    is_changed = _update_builddir()

                if not be.builddir.startswith("/"):
                    print("\n -- Validation: The build directory must to be set to an absolute path.")
                    is_changed = _update_builddir()

                if is_changed:
                    print("\nBuild configuration saved")
                    be.save()
                    return True

                if be.needs_import:
                    try:
                        print("Loading default settings")
                        call_command("loaddata", "settings")
                        template_conf = os.environ.get("TEMPLATECONF", "")
                        custom_xml_only = os.environ.get("CUSTOM_XML_ONLY")

                        if ToasterSetting.objects.filter(name='CUSTOM_XML_ONLY').count() > 0 or (not custom_xml_only == None):
                            # only use the custom settings
                            pass
                        elif "poky" in template_conf:
                            print("Loading poky configuration")
                            call_command("loaddata", "poky")
                        else:
                            print("Loading OE-Core configuration")
                            call_command("loaddata", "oe-core")
                            if template_conf:
                                oe_core_path = os.path.realpath(
                                    template_conf +
                                    "/../")
                            else:
                                print("TEMPLATECONF not found. You may have to"
                                      " manually configure layer paths")
                                oe_core_path = input("Please enter the path of"
                                                     " your openembedded-core "
                                                     "layer: ")
                            # Update the layer instances of openemebedded-core
                            for layer in Layer.objects.filter(
                                    name="openembedded-core",
                                    local_source_dir="OE-CORE-LAYER-DIR"):
                                layer.local_path = oe_core_path
                                layer.save()

                        # Import the custom fixture if it's present
                        with warnings.catch_warnings():
                            warnings.filterwarnings(
                                action="ignore",
                                message="^.*No fixture named.*$")
                            print("Importing custom settings if present")
                            try:
                                call_command("loaddata", "custom")
                            except:
                                print("NOTE: optional fixture 'custom' not found")

                        # we run lsupdates after config update
                        print("\nFetching information from the layer index, "
                              "please wait.\nYou can re-update any time later "
                              "by running bitbake/lib/toaster/manage.py "
                              "lsupdates\n")
                        call_command("lsupdates")

                        # we don't look for any other config files
                        return is_changed
                    except Exception as e:
                        print("Failure while trying to setup toaster: %s"
                              % e)
                        traceback.print_exc()

                return is_changed

            while _verify_be():
                pass
        return 0

    def _verify_default_settings(self):
        # verify that default settings are there
        if ToasterSetting.objects.filter(name='DEFAULT_RELEASE').count() != 1:
            ToasterSetting.objects.filter(name='DEFAULT_RELEASE').delete()
            ToasterSetting.objects.get_or_create(name='DEFAULT_RELEASE', value='')
        return 0

    def _verify_builds_in_progress(self):
        # we are just starting up. we must not have any builds in progress, or build environments taken
        for b in BuildRequest.objects.filter(state=BuildRequest.REQ_INPROGRESS):
            BRError.objects.create(req=b, errtype="toaster",
                                   errmsg=
                                   "Toaster found this build IN PROGRESS while Toaster started up. This is an inconsistent state, and the build was marked as failed")

        BuildRequest.objects.filter(state=BuildRequest.REQ_INPROGRESS).update(state=BuildRequest.REQ_FAILED)

        BuildEnvironment.objects.update(lock=BuildEnvironment.LOCK_FREE)

        # also mark "In Progress builds as failures"
        from django.utils import timezone
        Build.objects.filter(outcome=Build.IN_PROGRESS).update(outcome=Build.FAILED, completed_on=timezone.now())

        return 0



    def handle(self, **options):
        retval = 0
        retval += self._verify_build_environment()
        retval += self._verify_default_settings()
        retval += self._verify_builds_in_progress()

        return retval
