from django.contrib import messages
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from libvirt import libvirtError

from admin.decorators import superuser_only
from computes.models import Compute
from networks.forms import AddNetPool
from vrtManager.network import network_size, wvmNetwork, wvmNetworks


@superuser_only
def networks(request, compute_id):
    """
    :param request:
    :param compute_id:
    :return:
    """

    compute = get_object_or_404(Compute, pk=compute_id)
    errors = False

    try:
        conn = wvmNetworks(
            compute.hostname,
            compute.login,
            compute.password,
            compute.type,
        )
        networks = conn.get_networks_info()
        dhcp4 = netmask4 = gateway4 = ""
        dhcp6 = prefix6 = gateway6 = ""
        ipv4 = ipv6 = False

        if request.method == "POST":
            if "create" in request.POST:
                form = AddNetPool(request.POST)
                if form.is_valid():
                    data = form.cleaned_data
                    if data["name"] in networks:
                        msg = _("Network pool name already in use")
                        messages.error(request, msg)
                        errors = True
                    if data["forward"] in ["bridge", "macvtap"] and data["bridge_name"] == "":
                        messages.error(request, _("Please enter bridge/dev name"))
                        errors = True
                    if data["subnet"]:
                        ipv4 = True
                        gateway4, netmask4, dhcp4 = network_size(data["subnet"], data["dhcp4"])
                    if data["subnet6"]:
                        ipv6 = True
                        gateway6, prefix6, dhcp6 = network_size(data["subnet6"], data["dhcp6"])
                        if prefix6 != "64":
                            messages.error(
                                request, _("For libvirt, the IPv6 network prefix must be /64")
                            )
                            errors = True
                    if not errors:
                        conn.create_network(
                            data["name"],
                            data["forward"],
                            ipv4,
                            gateway4,
                            netmask4,
                            dhcp4,
                            ipv6,
                            gateway6,
                            prefix6,
                            dhcp6,
                            data["bridge_name"],
                            data["openvswitch"],
                            data["fixed"],
                        )
                        return HttpResponseRedirect(
                            reverse("network", args=[compute_id, data["name"]])
                        )
                else:
                    for msg_err in form.errors.values():
                        messages.error(request, msg_err.as_text())
        conn.close()
    except libvirtError as lib_err:
        messages.error(request, lib_err)

    return render(request, "networks.html", locals())


@superuser_only
def network(request, compute_id, pool):
    """
    :param request:
    :param compute_id:
    :param pool:
    :return:
    """

    compute = get_object_or_404(Compute, pk=compute_id)

    try:
        conn = wvmNetwork(
            compute.hostname,
            compute.login,
            compute.password,
            compute.type,
            pool,
        )
        networks = conn.get_networks()
        state = conn.is_active()
        device = conn.get_bridge_device()
        autostart = conn.get_autostart()
        net_mac = conn.get_network_mac()
        net_forward = conn.get_network_forward()
        qos = conn.get_qos()
        dhcp_range_start = ipv4_dhcp_range_end = dict()

        ip_networks = conn.get_ip_networks()
        for family, ip_network in ip_networks.items():
            if family == "ipv4":
                ipv4_dhcp_range_start = conn.get_dhcp_range_start(family)
                ipv4_dhcp_range_end = conn.get_dhcp_range_end(family)
                ipv4_network = ip_network
                ipv4_fixed_address = conn.get_dhcp_host_addr(family)
            elif family == "ipv6":
                ipv6_dhcp_range_start = conn.get_dhcp_range_start(family)
                ipv6_dhcp_range_end = conn.get_dhcp_range_end(family)
                ipv6_network = ip_network
                ipv6_fixed_address = conn.get_dhcp_host_addr(family)
            else:
                raise Exception(_("Unknown Network Family"))

        xml = conn._XMLDesc(0)
    except libvirtError as lib_err:
        messages.error(request, lib_err)
        return HttpResponseRedirect(reverse("networks", args=compute_id))

    if request.method == "POST":
        if "start" in request.POST:
            try:
                conn.start()
                return HttpResponseRedirect(request.get_full_path())
            except libvirtError as lib_err:
                messages.error(request, lib_err)
        if "stop" in request.POST:
            try:
                conn.stop()
                return HttpResponseRedirect(request.get_full_path())
            except libvirtError as lib_err:
                messages.error(request, lib_err)
        if "delete" in request.POST:
            try:
                conn.delete()
                return HttpResponseRedirect(reverse("networks", args=[compute_id]))
            except libvirtError as lib_err:
                messages.error(request, lib_err)
        if "set_autostart" in request.POST:
            try:
                conn.set_autostart(1)
                return HttpResponseRedirect(request.get_full_path())
            except libvirtError as lib_err:
                messages.error(request, lib_err)
        if "unset_autostart" in request.POST:
            try:
                conn.set_autostart(0)
                return HttpResponseRedirect(request.get_full_path())
            except libvirtError as lib_err:
                messages.error(request, lib_err)
        if "modify_fixed_address" in request.POST:
            name = request.POST.get("name", "")
            address = request.POST.get("address", "")
            family = request.POST.get("family", "ipv4")

            if family == "ipv4":
                mac_duid = request.POST.get("mac", "")
            if family == "ipv6":
                mac_duid = request.POST.get("id", "")

            try:
                ret_val = conn.modify_fixed_address(name, address, mac_duid, family)
                messages.success(request, _("Fixed address operation completed for %(family)s") % {"family": family.upper()})
                return HttpResponseRedirect(request.get_full_path())
            except libvirtError as lib_err:
                messages.error(request, lib_err)
            except ValueError as val_err:
                messages.error(request, val_err)
        if "delete_fixed_address" in request.POST:
            ip = request.POST.get("address", "")
            family = request.POST.get("family", "ipv4")
            conn.delete_fixed_address(ip, family)
            messages.success(request, _("%(family)s Fixed Address is Deleted.") % {"family": family.upper()})
            return HttpResponseRedirect(request.get_full_path())
        if "modify_dhcp_range" in request.POST:
            range_start = request.POST.get("range_start", "")
            range_end = request.POST.get("range_end", "")
            family = request.POST.get("family", "ipv4")
            try:
                conn.modify_dhcp_range(range_start, range_end, family)
                messages.success(request, _("%(family)s DHCP Range is Changed.") % {"family": family.upper()})
                return HttpResponseRedirect(request.get_full_path())
            except libvirtError as lib_err:
                messages.error(request, lib_err)
        if "edit_network" in request.POST:
            edit_xml = request.POST.get("edit_xml", "")
            if edit_xml:
                conn.edit_network(edit_xml)
                if conn.is_active():
                    messages.success(
                        request,
                        _(
                            "Network XML is changed. \\"
                            "Stop and start network to activate new config."
                        ),
                    )
                else:
                    messages.success(request, _("Network XML is changed."))
                return HttpResponseRedirect(request.get_full_path())
        if "set_qos" in request.POST:
            qos_dir = request.POST.get("qos_direction", "")
            average = request.POST.get("qos_average") or 0
            peak = request.POST.get("qos_peak") or 0
            burst = request.POST.get("qos_burst") or 0

            try:
                conn.set_qos(qos_dir, average, peak, burst)
                if conn.is_active():
                    messages.success(
                        request,
                        _("%(qos_dir)s QoS is updated. Network XML is changed. Stop and start network to activate new config") % {"qos_dir": qos_dir.capitalize()}
                    )
                else:
                    messages.success(request, _("%(qos_dir)s QoS is set") % {"qos_dir": qos_dir.capitalize()})
            except libvirtError as lib_err:
                messages.error(request, lib_err)
            return HttpResponseRedirect(request.get_full_path())
        if "unset_qos" in request.POST:
            qos_dir = request.POST.get("qos_direction", "")
            conn.unset_qos(qos_dir)

            if conn.is_active():
                messages.success(
                    request,
                    _("%(qos_dir)s QoS is deleted. Network XML is changed. \
                        Stop and start network to activate new config") % {"qos_dir": qos_dir.capitalize()}
                )
            else:
                messages.success(request, _("%(qos_dir)s QoS is deleted") % {"qos_dir": qos_dir.capitalize()})
            return HttpResponseRedirect(request.get_full_path())
    conn.close()

    return render(request, "network.html", locals())