from netbox_proxmox_sync.api.config import NB_API
from netbox_proxmox_sync.api.utils.errors import ValidationError
from netbox_proxmox_sync.api.proxmox.extract import Proxmox
from netbox_proxmox_sync.api.netbox.extract import NetBox


# MERGE, get created_*, updated_*, deleted_*
def tags():
    proxmox_tags = Proxmox.fetch_tags()
    netbox_tags = NetBox.fetch_tags()
    px_tags = {tag['name']: tag for tag in proxmox_tags}
    nb_tags = {tag['name']: tag for tag in netbox_tags}
    # "merge" information, extract stuff to C/U/D
    created = [tag for tag in proxmox_tags if tag['name'] not in nb_tags]
    deleted = [tag for tag in netbox_tags if tag['name'] not in px_tags]
    updated = []
    for name in px_tags:
        # only thing that can really change for tags is their color
        if name in nb_tags and px_tags[name]['color'] != nb_tags[name]['color']:
            nb_tags[name]['color'] = px_tags[name]['color']
            updated.append(nb_tags[name])
    # FIXME: error handling
    NB_API.extras.tags.delete([d['id'] for d in deleted])
    NB_API.extras.tags.update(updated)
    NB_API.extras.tags.create(created)
    return created, updated, deleted


# TODO: allow for updates of custom fields in case the plugin is updated to use more
# def custom_fields():


def vms(proxmox_vms, netbox_vms):
    px_vms = {vm['custom_fields']['vmid']: vm for vm in proxmox_vms}
    nb_vms = {vm['custom_fields']['vmid']: vm for vm in netbox_vms}
    # "merge" information, extract stuff to C/U/D
    created = [vm for vm in proxmox_vms if vm['custom_fields']['vmid'] not in nb_vms]
    deleted = [vm for vm in netbox_vms if vm['custom_fields']['vmid'] not in px_vms]
    updated = []
    for name in px_vms:
        changed = False
        px_vm = px_vms[name]
        if nb_vms.get(name) is None:
            continue
        nb_vm = nb_vms[name]
        # Update all fields
        for field in px_vm:
            if px_vm.get(field) is not None:
                changed |= nb_vm[field] != px_vm[field]
                nb_vm[field] = px_vm[field]
        if changed:
            updated.append(nb_vm)
    return created, updated, deleted


def interfaces(proxmox_interfaces, netbox_interfaces):
    # Note: if the MAC changes the IP for the VM is lost :)
    px_interfaces = {i['mac_address']: i for i in proxmox_interfaces}
    nb_interfaces = {i['mac_address']: i for i in netbox_interfaces}
    created = [i for i in proxmox_interfaces if i['mac_address'] not in nb_interfaces]
    deleted = [i for i in netbox_interfaces if i['mac_address'] not in px_interfaces]
    updated = []
    for name in px_interfaces:
        changed = False
        # Update all fields
        px_interface = px_interfaces[name]
        if nb_interfaces.get(name) is None:
            continue
        nb_interface = nb_interfaces[name]
        for field in px_interface:
            changed |= nb_interface[field] != px_interface[field]
            nb_interface[field] = px_interface[field]
        if changed:
            updated.append(nb_interface)
    return created, updated, deleted


def vms_and_interfaces(cluster_id):
    proxmox_vms, proxmox_interfaces = Proxmox.fetch_virtual_machines_and_interfaces()
    netbox_vms, netbox_interfaces = NetBox.fetch_virtual_machines_and_interfaces(cluster_id)

    created_vms, updated_vms, deleted_vms = vms(proxmox_vms, netbox_vms)
    created_i, updated_i, deleted_i = interfaces(proxmox_interfaces, netbox_interfaces)

    # FIXME: error handling
    NB_API.virtualization.virtual_machines.delete([d['id'] for d in deleted_vms])
    NB_API.virtualization.virtual_machines.update(updated_vms)
    NB_API.virtualization.virtual_machines.create(created_vms)
    # NB_API.virtualization.interfaces.delete([d['id'] for d in deleted])
    NB_API.virtualization.interfaces.update(updated_i)
    NB_API.virtualization.interfaces.create(created_i)

    return created_vms, updated_vms, deleted_vms, \
        created_i, updated_i, deleted_i


def all():
    proxmox_cluster = Proxmox.fetch_cluster()
    cluster_name = proxmox_cluster['name']
    clusters = list(NB_API.virtualization.clusters.filter(name=cluster_name))
    if (len(clusters)) == 0:
        raise ValidationError(f'Cluster "{cluster_name}" does not exist!')
    netbox_cluster = clusters[0]
    created_tags, updated_tags, deleted_tags = tags()
    created_vms, updated_vms, deleted_vms, \
        created_interfaces, updated_interfaces, deleted_interfaces = \
        vms_and_interfaces(cluster_id=netbox_cluster.id)
    return {
        'tags': {
            'created': created_tags,
            'updated': updated_tags,
            'deleted': deleted_tags,
        },
        'virtual_machines': {
            'created': created_vms,
            'updated': updated_vms,
            'deleted': deleted_vms,
        },
        'interfaces': {
            'created': created_interfaces,
            'updated': updated_interfaces,
            'deleted': deleted_interfaces,
        }
    }