import argparse
import logging
import re

import ldap3


from uncloud.hack.vm import VM
from uncloud.hack.host import Host
from uncloud.hack.config import Config
from uncloud.hack.mac import MAC
from uncloud.hack.net import VXLANBridge, DNSRA

from uncloud import UncloudException
from uncloud.hack.product import ProductOrder

arg_parser = argparse.ArgumentParser('hack', add_help=False)
                                     #description="Commands that are unfinished - use at own risk")
arg_parser.add_argument('--last-used-mac', action='store_true')
arg_parser.add_argument('--get-new-mac', action='store_true')

arg_parser.add_argument('--init-network', help="Initialise networking", action='store_true')
arg_parser.add_argument('--create-vxlan', help="Initialise networking", action='store_true')
arg_parser.add_argument('--network', help="/64 IPv6 network")
arg_parser.add_argument('--vxlan-uplink-device', help="The VXLAN underlay device, i.e. eth0")
arg_parser.add_argument('--vni', help="VXLAN ID (decimal)", type=int)
arg_parser.add_argument('--run-dns-ra', action='store_true',
                        help="Provide router advertisements and DNS resolution via dnsmasq")
arg_parser.add_argument('--use-sudo', help="Use sudo for command requiring root!", action='store_true')

arg_parser.add_argument('--create-vm', action='store_true')
arg_parser.add_argument('--destroy-vm', action='store_true')
arg_parser.add_argument('--get-vm-status', action='store_true')
arg_parser.add_argument('--get-vm-vnc', action='store_true')
arg_parser.add_argument('--list-vms', action='store_true')
arg_parser.add_argument('--memory', help="Size of memory (GB)", type=int, default=2)
arg_parser.add_argument('--cores', help="Amount of CPU cores", type=int, default=1)
arg_parser.add_argument('--image', help="Path (under hackprefix) to OS image")

arg_parser.add_argument('--image-format', help="Image format: qcow2 or raw", choices=['raw', 'qcow2'])
arg_parser.add_argument('--uuid', help="VM UUID")

arg_parser.add_argument('--no-db', help="Disable connection to etcd. For local testing only!", action='store_true')
arg_parser.add_argument('--hackprefix', help="hackprefix, if you need it you know it (it's where the iso is located and ifup/down.sh")

# order based commands => later to be shifted below "order"
arg_parser.add_argument('--order', action='store_true')
arg_parser.add_argument('--list-orders', help="List all orders", action='store_true')
arg_parser.add_argument('--filter-order-key', help="Which key to filter on")
arg_parser.add_argument('--filter-order-regexp', help="Which regexp the value should match")

arg_parser.add_argument('--process-orders', help="Process all (pending) orders", action='store_true')

arg_parser.add_argument('--product', choices=["dualstack-vm"])
arg_parser.add_argument('--os-image-name', help="Name of OS image (successor to --image)")
arg_parser.add_argument('--os-image-size', help="Size of OS image in GB", type=int, default=10)

arg_parser.add_argument('--username')
arg_parser.add_argument('--password')

arg_parser.add_argument('--api', help="Run the API")
arg_parser.add_argument('--mode',
                        choices=["direct", "api", "client"],
                        default="client",
                        help="Directly manipulate etcd, spawn the API server or behave as a client")


arg_parser.add_argument('--add-vm-host', help="Add a host that can run VMs")
arg_parser.add_argument('--list-vm-hosts', action='store_true')

arg_parser.add_argument('--max-cores-per-vm')
arg_parser.add_argument('--max-cores-total')
arg_parser.add_argument('--max-memory-in-gb')


log = logging.getLogger(__name__)

def authenticate(username, password, totp_token=None):
    server = ldap3.Server("ldaps://ldap1.ungleich.ch")
    dn = "uid={},ou=customer,dc=ungleich,dc=ch".format(username)

    log.debug("LDAP: connecting to {} as {}".format(server, dn))

    try:
        conn = ldap3.Connection(server, dn, password, auto_bind=True)
    except ldap3.core.exceptions.LDAPBindError as e:
        raise UncloudException("Credentials not verified by LDAP server: {}".format(e))



def order(config):
    for required_arg in [ 'product', 'username', 'password' ]:
        if not config.arguments[required_arg]:
            raise UncloudException("Missing required argument: {}".format(required_arg))

    if config.arguments['product'] == 'dualstack-vm':
        for required_arg in [ 'cores', 'memory', 'os_image_name', 'os_image_size' ]:
            if not config.arguments[required_arg]:
                raise UncloudException("Missing required argument: {}".format(required_arg))

        log.debug(config.arguments)
        authenticate(config.arguments['username'], config.arguments['password'])

        # create DB entry for VM
        vm = VM(config)
        return vm.product.place_order(owner=config.arguments['username'])





def main(arguments):
    config = Config(arguments)

    if arguments['add_vm_host']:
        h = Host(config)
        h.cmdline_add_host()

    if arguments['list_vm_hosts']:
        h = Host(config)

        for host in h.list_hosts(filter_key=arguments['filter_order_key'],
                                           filter_regexp=arguments['filter_order_regexp']):
            print("Host {}: {}".format(host.db_entry['uuid'], host.db_entry))

    if arguments['order']:
        print("Created order: {}".format(order(config)))

    if arguments['list_orders']:
        p = ProductOrder(config)
        for product_order in p.list_orders(filter_key=arguments['filter_order_key'],
                                           filter_regexp=arguments['filter_order_regexp']):
            print("Order {}: {}".format(product_order.db_entry['uuid'], product_order.db_entry))

    if arguments['process_orders']:
        p = ProductOrder(config)
        p.process_orders()

    if arguments['create_vm']:
        vm = VM(config)
        vm.create()

    if arguments['destroy_vm']:
        vm = VM(config)
        vm.stop()

    if arguments['get_vm_status']:
        vm = VM(config)
        vm.status()

    if arguments['get_vm_vnc']:
        vm = VM(config)
        vm.vnc_addr()

    if arguments['list_vms']:
        vm = VM(config)
        vm.list()

    if arguments['last_used_mac']:
        m = MAC(config)
        print(m.last_used_mac())

    if arguments['get_new_mac']:
        print(MAC(config).get_next())

    #if arguments['init_network']:
    if arguments['create_vxlan']:
        if not arguments['network'] or not arguments['vni'] or not arguments['vxlan_uplink_device']:
            raise UncloudException("Initialising the network requires an IPv6 network and a VNI. You can use fd00::/64 and vni=1 for testing (non production!)")
        vb = VXLANBridge(vni=arguments['vni'],
                         route=arguments['network'],
                         uplinkdev=arguments['vxlan_uplink_device'],
                         use_sudo=arguments['use_sudo'])
        vb._setup_vxlan()
        vb._setup_bridge()
        vb._add_vxlan_to_bridge()
        vb._route_network()

    if arguments['run_dns_ra']:
        if not arguments['network'] or not arguments['vni']:
            raise UncloudException("Providing DNS/RAs requires a /64 IPv6 network and a VNI. You can use fd00::/64 and vni=1 for testing (non production!)")

        dnsra = DNSRA(route=arguments['network'],
                      vni=arguments['vni'],
                      use_sudo=arguments['use_sudo'])
        dnsra._setup_dnsmasq()
