#!/usr/bin/env python
"""
NetFoundry Router Upgrade
"""
import json
import sys
import os
import logging
import argparse
import yaml
import semantic_version as sv
import reg_common


def do_upgrade(ziti_version,
               arm_machine,
               product_metadata,
               auto=False,
               override=False):
    """
    Upgrade
    """
    logging.debug("Starting Upgrade")


    if os.path.exists("/opt/netfoundry/ziti/ziti-router/ziti-router"):
        current_version = get_ziti_version("ziti-router")
        print("ziti-router version:", current_version)
    if os.path.exists("/opt/netfoundry/ziti/ziti-tunnel/ziti-tunnel"):
        current_version = get_ziti_version("ziti-tunnel")
        print("ziti-tunnel version:", current_version)
    if os.path.exists("/opt/netfoundry/ziti/ziti"):
        current_version = get_ziti_version("ziti")
        print("ziti version:", current_version)
    if not override:
        print("ControllerVersion: ", ziti_version)

    download_url = reg_common.parse_metadata(product_metadata,
                                             arm_machine,
                                             "zitiBinaryBundleLinuxAMD64")

    # compare and process if necessary
    if sv.Version(current_version) < sv.Version(ziti_version) or override:
        if override:
            print("Override Requested, forcing version: " + ziti_version)
        else:
            print("Controller is higher than Local")

        if not auto:
            upgrade = query_yes_no("Would you like to proceed with the upgrade?")
        else:
            upgrade = True


        if upgrade:
            binary_list = ["ziti-router", "ziti-tunnel"]
            # stop services
            for binary_name in binary_list:
                # stop service
                ziti_services("stop", binary_name)

            # download and extract file
            reg_common.download_bundle(download_url)

            for binary_name in binary_list:
                # check if service is enabled
                service_enabled = reg_common.run_cmd(["systemctl", "is-enabled", binary_name])
                if "enabled" in service_enabled.stdout:
                    ziti_services("start", binary_name)
                    logging.debug("Started Service: %s", binary_name)
            print("Upgraded successfully")
    else:
        print("Already up to date")

def extract_router_controller_ip(config):
    """
    Get the controller ip from the con
    """
    logging.debug("Trying to open yaml config")
    with open(config, "r") as file:
        try:
            yaml_content = yaml.load(file, Loader=yaml.FullLoader)
            controller_info = yaml_content["ctrl"]["endpoint"]
        except yaml.YAMLError:
            logging.debug("Unable to read config format")
    controller_ip = controller_info.split(":")[1]
    logging.debug("Controller IP: %s", controller_ip)

    return controller_ip

def extract_tunnel_controller_ip(config):
    """
    Get the controller ip from the config
    """
    logging.debug("Trying to open yaml config")
    with open(config, "r") as file:
        try:
            json_content = json.loads(file.read())
            controller_info = json_content["ztAPI"]
        except json.JSONDecodeError:
            logging.debug("Unable to read config format")
    controller_ip = controller_info.split(":")[1].lstrip("/")
    logging.debug("Controller IP: %s", controller_ip)

    return controller_ip

def get_ziti_version(binary_name):
    """
    Get the local version of a ziti binary
    """
    logging.debug("Running %s version command for: ", binary_name)
    if binary_name == "ziti":
        version_output = reg_common.run_cmd(["/opt/netfoundry/ziti/ziti",
                                             "--version"])
    elif binary_name == "ziti-router":
        version_output = reg_common.run_cmd(["/opt/netfoundry/ziti/ziti-router/ziti-router",
                                             "version"])
    elif binary_name == "ziti-tunnel":
        version_output = reg_common.run_cmd(["/opt/netfoundry/ziti/ziti-tunnel/ziti-tunnel",
                                             "version"])
    version = version_output.stdout.split("v")[1]
    logging.debug("%s version: %s", binary_name, version)
    return version.strip()

def exit_gracefully():
    """
    Exit gracefully on Keyboard Interrupt
    """
    print('\nDetected Ctrl+C! exiting script!')
    sys.exit(0)

def query_yes_no(question, default="yes"):
    """This function handles the yes/no questions"""
    valid = {"yes": True, "y": True,
             "no": False, "n": False}
    if default is None:
        prompt = " [y/n] "
    elif default == "yes":
        prompt = " [Y/n] "
    elif default == "no":
        prompt = " [y/N] "
    else:
        raise ValueError("invalid default answer: '%s'" % default)

    while True:
        sys.stdout.write(question + prompt)
        choice = input().lower()
        if default is not None and choice == '':
            return valid[default]
        elif choice in valid:
            return valid[choice]
        else:
            sys.stdout.write("Please respond with 'yes' or 'no' "
                             "(or 'y' or 'n').\n")

def ziti_services(action, service):
    """
    Start ziti-router services
    """
    if action == "start":
        logging.debug("Starting the service: %s", service)
        reg_common.run_cmd(["systemctl", "restart", service])

    elif action == "stop":
        logging.debug("Stopping the service: %s", service)
        try:
            reg_common.run_cmd(["systemctl", "stop", service])
        except OSError:
            # shouldn't be here.. but if it is..let's keep going
            logging.debug("Stopping the %s service failed", service)

def main():
    """
    Main logic
    """
    __version__ = '4.1.0'
    # change log
    # 1.0.0 - initial release
    # 1.1.0 - add arm support
    # 2.0.0 - combine router-upgrade and tunnel-upgrade; add ziti cli upgrade
    # 2.0.1 - bugfix error when only tunnel is present
    # 2.1.0 - prompt before upgrade unless passing in -y flag; add -o to override version
    # 3.0.0 - get download url from MOP
    # 3.0.1 - don't error if tunnel is not configured.
    # 3.1.0 - split reg_common; add check for locally cached binaries
    # 4.0.0 - switch from product-metadata to network-version endpoint
    # 4.1.0 - change download to bundle

    # argument parser
    parser = argparse.ArgumentParser()
    # arguments
    parser.add_argument('-y', '--yes',
                        dest='auto',
                        action='store_true',
                        help='automatically answer yes to upgrade prompt')
    parser.add_argument('-o', '--override_version',
                        help='override the controller version')
    parser.add_argument('-d', '--debug',
                        action='store_true',
                        help='enable debug log in log file output')
    parser.add_argument('-v', '--version',
                        action='version',
                        version=__version__)

    # get arguments passed
    args = parser.parse_args()

    # enable debug if requested
    if args.debug:
        log_level = logging.DEBUG
    else:
        log_level = logging.INFO

    # define logging information
    logging.basicConfig(format='%(asctime)s-%(levelname)s-%(message)s',
                        datefmt='%Y-%m-%d-%H:%M:%S',
                        level=log_level
                        )

    # run root check
    reg_common.root_check()

    auto_upgrade = bool(args.auto)

    # set architecture
    arm_machine = reg_common.arm_check()

    # upgrade router
    if os.path.exists("/opt/netfoundry/ziti/ziti-router/ziti-router"):
        # check if ziti config exits
        router_config = "/opt/netfoundry/ziti/ziti-router/config.yml"
        if os.path.isfile(router_config):
            # get controller ip
            controller_ip = extract_router_controller_ip(router_config)
    elif os.path.exists("/opt/netfoundry/ziti/ziti-tunnel/ziti-tunnel"):
        # check if ziti config exits
        tunnel_config = "/opt/netfoundry/ziti/ziti-tunnel/config.json"
        if os.path.isfile(tunnel_config):

            # get controller ip
            controller_ip = extract_tunnel_controller_ip(tunnel_config)
    else:
        print("Unable to find Router or Tunneler to upgrade")
        sys.exit(1)

    # determine version
    if args.override_version:
        ziti_version = args.override_version
        override = True
    else:
        ziti_version = reg_common.get_ziti_controller_version("https://" + controller_ip)
        override = False

    product_metadata = reg_common.get_mop_product_metadata(ziti_version)

    do_upgrade(ziti_version,
                arm_machine,
                product_metadata,
                auto_upgrade,
                override)

    # exit properly
    sys.exit(0)

# main
if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        exit_gracefully()
