##############################################################################
# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################


from django.forms import formset_factory, modelformset_factory

from workflow.models import WorkflowStep
from workflow.forms import SoftwareConfigurationForm, HostSoftwareDefinitionForm
from workflow.booking_workflow import Resource_Select
from resource_inventory.models import *


#resource selection step is reused from Booking workflow

#TODO: change this: too hacky, just for presentation

class SWConf_Resource_Select(Resource_Select):
    def __init__(self, *args, **kwargs):
        super(SWConf_Resource_Select, self).__init__(*args, **kwargs)
        self.repo_key = self.repo.SWCONF_SELECTED_GRB
        self.confirm_key = "configuration"

    def get_default_entry(self):
        booking_grb = self.repo_get(self.repo.BOOKING_SELECTED_GRB)
        if booking_grb:
            return booking_grb
        created_grb = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {}).get("bundle", None)
        return created_grb

    def post_render(self, request):
        response = super(SWConf_Resource_Select, self).post_render(request)
        models = self.repo_get(self.repo.CONFIG_MODELS, {})
        bundle = models.get("bundle", ConfigBundle(owner=self.repo_get(self.repo.SESSION_USER)))
        bundle.bundle = self.repo_get(self.repo_key)  # super put grb here
        models['bundle'] = bundle
        self.repo_put(self.repo.CONFIG_MODELS, models)
        return response

class Define_Software(WorkflowStep):
    template = 'config_bundle/steps/define_software.html'
    title = "Pick Software"
    description = "Choose the opnfv and image of your machines"
    short_title = "host config"

    def create_hostformset(self, hostlist):
        hosts_initial = []
        host_configs = self.repo_get(self.repo.CONFIG_MODELS, {}).get("host_configs", False)
        if host_configs:
            for config in host_configs:
                host_initial = {'host_id': config.host.id, 'host_name': config.host.resource.name}
                host_initial['role'] = config.opnfvRole
                host_initial['image'] = config.image
                hosts_initial.append(host_initial)

        else:
            for host in hostlist:
                host_initial = {'host_id': host.id, 'host_name': host.resource.name}

                hosts_initial.append(host_initial)

        HostFormset = formset_factory(HostSoftwareDefinitionForm, extra=0)
        host_formset = HostFormset(initial=hosts_initial)

        filter_data = {}
        user = self.repo_get(self.repo.SESSION_USER)
        i=0;
        for host_data in hosts_initial:
            host = GenericHost.objects.get(pk=host_data['host_id'])
            excluded_images = Image.objects.exclude(owner=user).exclude(public=True)
            excluded_images = excluded_images | Image.objects.exclude(host_type=host.profile)
            lab = self.repo_get(self.repo.SWCONF_SELECTED_GRB).lab
            excluded_images = excluded_images | Image.objects.exclude(from_lab=lab)
            filter_data["id_form-" + str(i) + "-image"] = []
            for image in excluded_images:
                filter_data["id_form-" + str(i) + "-image"].append(image.name)
            i += 1

        return host_formset, filter_data

    def get_host_list(self, grb=None):
        if grb is None:
            grb = self.repo_get(self.repo.SWCONF_SELECTED_GRB, False)
            if not grb:
                return []
        if grb.id:
            return GenericHost.objects.filter(resource__bundle=grb)
        generic_hosts = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {}).get("hosts", [])
        return generic_hosts

    def get_context(self):
        context = super(Define_Software, self).get_context()

        grb = self.repo_get(self.repo.SWCONF_SELECTED_GRB, False)

        if grb:
            context["grb"] = grb
            formset, filter_data = self.create_hostformset(self.get_host_list(grb))
            context["formset"] = formset
            context["filter_data"] = filter_data
        else:
            context["error"] = "Please select a resource first"
            self.metastep.set_invalid("Step requires information that is not yet provided by previous step")

        return context

    def post_render(self, request):
        models = self.repo_get(self.repo.CONFIG_MODELS, {})
        if "bundle" not in models:
            models['bundle'] = ConfigBundle(owner=self.repo_get(self.repo.SESSION_USER))

        confirm = self.repo_get(self.repo.CONFIRMATION, {})

        HostFormset = formset_factory(HostSoftwareDefinitionForm, extra=0)
        formset = HostFormset(request.POST)
        hosts = self.get_host_list()
        if formset.is_valid():
            models['host_configs'] = []
            i = 0
            confirm_hosts = []
            for form in formset:
                host = hosts[i]
                i += 1
                image = form.cleaned_data['image']
                # checks image compatability
                grb = self.repo_get(self.repo.SWCONF_SELECTED_GRB)
                lab = None
                if grb:
                    lab = grb.lab
                try:
                    owner = self.repo_get(self.repo.SESSION_USER)
                    q = Image.objects.filter(owner=owner) | Image.objects.filter(public=True)
                    q.filter(host_type=host.profile)
                    q.filter(from_lab=lab)
                    q.get(id=image.id)  # will throw exception if image is not in q
                except:
                    self.metastep.set_invalid("Image " + image.name + " is not compatible with host " + host.resource.name)
                role = form.cleaned_data['role']
                bundle = models['bundle']
                hostConfig = HostConfiguration(
                        host=host,
                        image=image,
                        bundle=bundle,
                        opnfvRole=role
                )
                models['host_configs'].append(hostConfig)
                confirm_host = {"name": host.resource.name, "image": image.name, "role": role.name}
                confirm_hosts.append(confirm_host)

            self.repo_put(self.repo.CONFIG_MODELS, models)
            if "configuration" not in confirm:
                confirm['configuration'] = {}
            confirm['configuration']['hosts'] = confirm_hosts
            self.repo_put(self.repo.CONFIRMATION, confirm)
            self.metastep.set_valid("Completed")
        else:
            self.metastep.set_invalid("Please complete all fields")

        return self.render(request)

class Config_Software(WorkflowStep):
    template = 'config_bundle/steps/config_software.html'
    form = SoftwareConfigurationForm
    context = {'workspace_form':form}
    title = "Other Info"
    description = "Give your software config a name, description, and other stuff"
    short_title = "config info"

    def get_context(self):
        context = super(Config_Software, self).get_context()

        initial = {}
        models = self.repo_get(self.repo.CONFIG_MODELS, {})
        bundle = models.get("bundle", False)
        if bundle:
            initial['name'] = bundle.name
            initial['description'] = bundle.description
        opnfv = models.get("opnfv", False)
        if opnfv:
            initial['installer'] = opnfv.installer
            initial['scenario'] = opnfv.scenario
        else:
            initial['opnfv'] = False
        supported = {}
        for installer in Installer.objects.all():
            supported[str(installer)] = []
            for scenario in installer.sup_scenarios.all():
                supported[str(installer)].append(str(scenario))

        context["form"] = SoftwareConfigurationForm(initial=initial)
        context['supported'] = supported

        return context

    def post_render(self, request):
        try:
            models = self.repo_get(self.repo.CONFIG_MODELS, {})
            if "bundle" not in models:
                models['bundle'] = ConfigBundle(owner=self.repo_get(self.repo.SESSION_USER))


            confirm = self.repo_get(self.repo.CONFIRMATION, {})
            if "configuration" not in confirm:
                confirm['configuration'] = {}

            form = self.form(request.POST)
            if form.is_valid():
                models['bundle'].name = form.cleaned_data['name']
                models['bundle'].description = form.cleaned_data['description']
                if form.cleaned_data['opnfv']:
                    installer = form.cleaned_data['installer']
                    scenario = form.cleaned_data['scenario']
                    opnfv = OPNFVConfig(
                            bundle=models['bundle'],
                            installer=installer,
                            scenario=scenario
                        )
                    models['opnfv'] = opnfv
                    confirm['configuration']['installer'] = form.cleaned_data['installer'].name
                    confirm['configuration']['scenario'] = form.cleaned_data['scenario'].name

                confirm['configuration']['name'] = form.cleaned_data['name']
                confirm['configuration']['description'] = form.cleaned_data['description']
                self.metastep.set_valid("Complete")
            else:
                self.metastep.set_invalid("Please correct the errors shown below")

            self.repo_put(self.repo.CONFIG_MODELS, models)
            self.repo_put(self.repo.CONFIRMATION, confirm)

        except Exception as e:
            pass
        return self.render(request)