##############################################################################
# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, 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.template.loader import render_to_string

import booking
from dashboard.exceptions import (
    ResourceExistenceException,
    ResourceAvailabilityException,
    ResourceProvisioningException,
    ModelValidationException,
)
from resource_inventory.models import Host, HostConfiguration, ResourceBundle


class ResourceManager:

    instance = None

    def __init__(self):
        pass

    @staticmethod
    def getInstance():
        if ResourceManager.instance is None:
            ResourceManager.instance = ResourceManager()
        return ResourceManager.instance

    # public interface
    def deleteResourceBundle(self, resourceBundle):
        for host in Host.objects.filter(bundle=resourceBundle):
            self.releaseHost(host)
        resourceBundle.delete()

    def convertResourceBundle(self, genericResourceBundle, lab=None, config=None):
        """
        Takes in a GenericResourceBundle and 'converts' it into a ResourceBundle
        """
        resource_bundle = ResourceBundle()
        resource_bundle.template = genericResourceBundle
        resource_bundle.save()

        hosts = genericResourceBundle.getHosts()

        # current supported case: user creating new booking
        # currently unsupported: editing existing booking

        physical_hosts = []

        for host in hosts:
            host_config = None
            if config:
                host_config = HostConfiguration.objects.get(bundle=config, host=host)
            try:
                physical_host = self.acquireHost(host, genericResourceBundle.lab.name)
            except ResourceAvailabilityException:
                self.fail_acquire(physical_hosts)
                raise ResourceAvailabilityException("Could not provision hosts, not enough available")
            try:
                physical_host.bundle = resource_bundle
                physical_host.template = host
                physical_host.config = host_config
                physical_hosts.append(physical_host)

                self.configureNetworking(physical_host)
            except:
                self.fail_acquire(physical_hosts)
                raise ResourceProvisioningException("Network configuration failed.")
            try:
                physical_host.save()
            except:
                self.fail_acquire(physical_hosts)
                raise ModelValidationException("Saving hosts failed")

        return resource_bundle

    def configureNetworking(self, host):
        generic_interfaces = list(host.template.generic_interfaces.all())
        for int_num, physical_interface in enumerate(host.interfaces.all()):
            generic_interface = generic_interfaces[int_num]
            physical_interface.config.clear()
            for vlan in generic_interface.vlans.all():
                physical_interface.config.add(vlan)

    # private interface
    def acquireHost(self, genericHost, labName):
        host_full_set = Host.objects.filter(lab__name__exact=labName, profile=genericHost.profile)
        if not host_full_set.first():
            raise ResourceExistenceException("No matching servers found")
        host_set = host_full_set.filter(booked=False)
        if not host_set.first():
            raise ResourceAvailabilityException("No unbooked hosts match requested hosts")
        host = host_set.first()
        host.booked = True
        host.template = genericHost
        host.save()
        return host

    def releaseHost(self, host):
        host.template = None
        host.bundle = None
        host.booked = False
        host.save()

    def fail_acquire(self, hosts):
        for host in hosts:
            self.releaseHost(host)

    def makePDF(self, resource):
        """
        fills the pod descriptor file template with info about the resource
        """
        template = "dashboard/pdf.yaml"
        info = {}
        info['details'] = self.get_pdf_details(resource)
        info['jumphost'] = self.get_pdf_jumphost(resource)
        info['nodes'] = self.get_pdf_nodes(resource)

        return render_to_string(template, context=info)

    def get_pdf_details(self, resource):
        details = {}
        owner = "Anon"
        email = "email@mail.com"
        resource_lab = resource.template.lab
        lab = resource_lab.name
        location = resource_lab.location
        pod_type = "development"
        link = "https://wiki.opnfv.org/display/INF/Pharos+Laas"

        try:
            # try to get more specific info that may fail, we dont care if it does
            booking_owner = booking.models.Booking.objects.get(resource=resource).owner
            owner = booking_owner.username
            email = booking_owner.userprofile.email_addr
        except Exception:
            pass

        details['owner'] = owner
        details['email'] = email
        details['lab'] = lab
        details['location'] = location
        details['type'] = pod_type
        details['link'] = link

        return details

    def get_pdf_jumphost(self, resource):
        jumphost = Host.objects.get(bundle=resource, config__opnfvRole__name__iexact="jumphost")
        return self.get_pdf_host(jumphost)

    def get_pdf_nodes(self, resource):
        pdf_nodes = []
        nodes = Host.objects.filter(bundle=resource).exclude(config__opnfvRole__name__iexact="jumphost")
        for node in nodes:
            pdf_nodes.append(self.get_pdf_host(node))

        return pdf_nodes

    def get_pdf_host(self, host):
        host_info = {}
        host_info['name'] = host.template.resource.name
        host_info['node'] = {}
        host_info['node']['type'] = "baremetal"
        host_info['node']['vendor'] = host.vendor
        host_info['node']['model'] = host.model
        host_info['node']['arch'] = host.profile.cpuprofile.first().architecture
        host_info['node']['cpus'] = host.profile.cpuprofile.first().cpus
        host_info['node']['cores'] = host.profile.cpuprofile.first().cores
        cflags = host.profile.cpuprofile.first().cflags
        if cflags and cflags.strip():
            host_info['node']['cpu_cflags'] = cflags
        host_info['node']['memory'] = str(host.profile.ramprofile.first().amount) + "G"
        host_info['disks'] = []
        for disk in host.profile.storageprofile.all():
            disk_info = {}
            disk_info['name'] = disk.name
            disk_info['capacity'] = str(disk.size) + "G"
            disk_info['type'] = disk.media_type
            disk_info['interface'] = disk.interface
            disk_info['rotation'] = disk.rotation
            host_info['disks'].append(disk_info)

        host_info['interfaces'] = []
        for interface in host.interfaces.all():
            iface_info = {}
            iface_info['name'] = interface.name
            iface_info['address'] = "unknown"
            iface_info['mac_address'] = interface.mac_address
            vlans = "|".join([str(vlan.vlan_id) for vlan in interface.config.all()])
            iface_info['vlans'] = vlans
            host_info['interfaces'].append(iface_info)

        return host_info