import subprocess
import ipaddress
import logging


from uncloud import UncloudException

log = logging.getLogger(__name__)


class VXLANBridge(object):
    cmd_create_vxlan = "{sudo}ip -6 link add {vxlandev} type vxlan id {vni_dec} dstport 4789 group {multicast_address} dev {uplinkdev} ttl 5"
    cmd_up_dev = "{sudo}ip link set {dev} up"
    cmd_create_bridge="{sudo}ip link add {bridgedev} type bridge"
    cmd_add_to_bridge="{sudo}ip link set {vxlandev} master {bridgedev} up"
    cmd_add_addr="{sudo}ip addr add {ip} dev {bridgedev}"
    cmd_add_route_dev="{sudo}ip route add {route} dev {bridgedev}"

    # VXLAN ids are at maximum 24 bit - use a /104
    multicast_network = ipaddress.IPv6Network("ff05::/104")
    max_vni = (2**24)-1

    def __init__(self,
                 vni,
                 uplinkdev,
                 route=None,
                 use_sudo=False):
        self.config = {}

        if vni > self.max_vni:
            raise UncloudException("VNI must be in the range of 0 .. {}".format(self.max_vni))

        if use_sudo:
            self.config['sudo'] = 'sudo '
        else:
            self.config['sudo'] = ''

        self.config['vni_dec'] = vni
        self.config['vni_hex'] = "{:x}".format(vni)
        self.config['multicast_address'] = self.multicast_network[vni]

        self.config['route_network'] = ipaddress.IPv6Network(route)
        self.config['route'] = route

        self.config['uplinkdev'] = uplinkdev
        self.config['vxlandev'] = "vx{}".format(self.config['vni_hex'])
        self.config['bridgedev'] = "br{}".format(self.config['vni_hex'])


    def setup_networking(self):
        pass

    def _setup_vxlan(self):
        self._execute_cmd(self.cmd_create_vxlan)
        self._execute_cmd(self.cmd_up_dev, dev=self.config['vxlandev'])

    def _setup_bridge(self):
        self._execute_cmd(self.cmd_create_bridge)
        self._execute_cmd(self.cmd_up_dev, dev=self.config['bridgedev'])

    def _route_network(self):
        self._execute_cmd(self.cmd_add_route_dev)

    def _add_vxlan_to_bridge(self):
        self._execute_cmd(self.cmd_add_to_bridge)

    def _execute_cmd(self, cmd_string, **kwargs):
        cmd = cmd_string.format(**self.config, **kwargs)
        log.info("Executing: {}".format(cmd))
        subprocess.run(cmd.split())

class ManagementBridge(VXLANBridge):
    pass


class DNSRA(object):
    # VXLAN ids are at maximum 24 bit
    max_vni = (2**24)-1


    # Command to start dnsmasq
    cmd_start_dnsmasq="{sudo}dnsmasq --interface={bridgedev} --bind-interfaces --dhcp-range={route},ra-only,infinite --enable-ra --no-daemon"

    def __init__(self,
                 vni,
                 route=None,
                 use_sudo=False):
        self.config = {}

        if vni > self.max_vni:
            raise UncloudException("VNI must be in the range of 0 .. {}".format(self.max_vni))

        if use_sudo:
            self.config['sudo'] = 'sudo '
        else:
            self.config['sudo'] = ''

        #TODO: remove if not needed
        #self.config['vni_dec'] = vni
        self.config['vni_hex'] = "{:x}".format(vni)

        # dnsmasq only wants the network without the prefix, therefore, cut it off
        self.config['route'] = ipaddress.IPv6Network(route).network_address
        self.config['bridgedev'] = "br{}".format(self.config['vni_hex'])

    def _setup_dnsmasq(self):
         self._execute_cmd(self.cmd_start_dnsmasq)

    def _execute_cmd(self, cmd_string, **kwargs):
        cmd = cmd_string.format(**self.config, **kwargs)
        log.info("Executing: {}".format(cmd))
        print("Executing: {}".format(cmd))
        subprocess.run(cmd.split())

class Firewall(object):
    pass