#!/usr/bin/python

from __future__ import unicode_literals
from __future__ import print_function

import argparse
import json
import os
import uuid
import sys


def which(program):
    def is_exe(fpath):
        return os.path.isfile(fpath) and os.access(fpath, os.X_OK)

    fpath, _fname = os.path.split(program)
    if fpath:
        if is_exe(program):
            return program
    else:
        for path in os.environ["PATH"].split(os.pathsep):
            path = path.strip('"')
            exe_file = os.path.join(path, program)
            if is_exe(exe_file):
                return exe_file

    return None


def setup(_parser, _args):
    if os.path.isfile("tcell_agent.config"):
        print("Error: Config file already exists!")
        return

    try:
        input_fn = raw_input
    except NameError:
        input_fn = input
    api_key = input_fn('Server-Agent-Scoped Api Key? >')
    app_id = input_fn('App Id? (you can get this from the URL in our app, something like: MyApp-dkdn3) >')

    config_json = {
        "version": 1,
        "applications": [
            {
                "app_id": app_id,
                "api_key": api_key
            }
        ]
    }

    with open("tcell_agent.config", 'w') as outfile:
        json.dump(config_json, outfile, indent=2)


def loglevel(_parser, args):
    level = args.level.upper()
    if not os.path.isfile("tcell_agent.config"):
        print("Error:Could not find config file")
        return
    try:
        with open("tcell_agent.config") as data_file:
            config_json = json.loads(data_file.read())
    except Exception as e:
        print("Error: Could not load the config file.")
        print(e)
        return

    applications = config_json.get("applications", [])
    if len(applications) == 0:
        print("Error: No application found.")
        return

    if level == "OFF":
        config_json["applications"][0]["logging_options"] = {
            "enabled": False
        }
    else:
        config_json["applications"][0]["logging_options"] = {
            "enabled": True,
            "level": level
        }

    with open("tcell_agent.config", 'w') as outfile:
        json.dump(config_json, outfile, indent=2)


def demomode(_parser, args):
    if not os.path.isfile("tcell_agent.config"):
        print("Error:Could not find config file")
        return
    try:
        with open("tcell_agent.config") as data_file:
            config_json = json.loads(data_file.read())
    except Exception as e:
        print("Error: Could not load the config file.")
        print(e)
        return

    applications = config_json.get("applications", [])
    if len(applications) == 0:
        print("Error: No application found.")
        return

    if args.off:
        if "demomode" in config_json["applications"][0]:
            del config_json["applications"][0]["demomode"]
    else:
        config_json["applications"][0]["demomode"] = True

    with open("tcell_agent.config", 'w') as outfile:
        json.dump(config_json, outfile, indent=2)


def report_packages(_parser, _args):
    try:
        from tcell_agent.config.configuration import get_config
        from tcell_agent.config.configuration_service import init_config
        from tcell_agent.rust.native_library import load_native_lib
        from tcell_agent.rust.basic_checks import test_event_sender
        from tcell_agent.events.server_agent_packages import ServerAgentPackagesEvent
        from tcell_agent.system_info import get_packages
        from tcell_agent.version import VERSION

        config = init_config()
        load_native_lib()

        print("Reporting packages ({0})".format(get_config().tcell_input_url))
        sape = ServerAgentPackagesEvent()

        def add_package_to_event(package_obj):
            print("\tfound {} {}".format(package_obj.key, package_obj.version))
            sape.add_package(package_obj.key, package_obj.version)
        get_packages(add_package_to_event)

        errors = test_event_sender(config,
                                   [sape],
                                   VERSION,
                                   str(uuid.uuid4()))
        if errors:
            map(print, errors)
        else:
            print("Done")

    except Exception as e:
        print(e)
        import traceback
        traceback.print_exc()


def test(_parser, _args):
    def printm(msg):
        template_msg = "{0:64}"
        print(template_msg.format(msg), end="")

    def prints(status):
        template_status = "[{0}]"
        print(template_status.format(status))

    def passed():
        prints("PASSED")

    def failed():
        prints("FAILED")

    config_file = "tcell_agent.config"

    printm("Checking if configuration file exists.")
    if not os.path.isfile(config_file):
        failed()
        exit(1)
    passed()

    printm("Checking that config json is valid.")
    try:
        with open(config_file) as data_file:
            config_json = json.loads(data_file.read())
    except Exception as e:
        failed()
        print(e)
        exit(1)
    passed()

    printm("Config file has valid version... ")
    if config_json.get("version", 0) != 1:
        failed()
        exit(1)
    passed()

    printm("Config file has application")
    applications = config_json.get("applications", [])
    if len(applications) == 0:
        failed()
        exit(1)
    passed()

    from tcell_agent.config.unknown_options import get_unknown_options  # pylint: disable=no-name-in-module
    printm("Check for unknown settings")
    messages = get_unknown_options(config_json)
    if len(messages) > 0:
        failed()
        for message in messages:
            print(message)
        exit(1)
    passed()

    printm("Loading tcell config")
    from tcell_agent.config.configuration_service import init_config  # pylint: disable=no-name-in-module
    try:
        config = init_config()
        passed()
    except Exception as e:
        failed()
        print(e)
        exit(1)

    printm("Application has api_key && app_id")
    if not (config.app_id and config.api_key):
        failed()
        exit(1)
    prints(config.app_id)

    from tcell_agent.rust.native_library import load_native_lib, version  # pylint: disable=no-name-in-module
    printm("Loading native library (v{0})".format(version))
    try:
        load_native_lib()
        passed()
    except Exception as e:
        failed()
        print(e)
        exit(1)

    printm("Fetching a policy ({0})".format(config.tcell_api_url))
    from tcell_agent.rust.basic_checks import test_policies  # pylint: disable=no-name-in-module
    try:
        errors = test_policies(config)
        if errors:
            failed()
            map(print, errors)
            exit(1)
        else:
            passed()
    except Exception as e:
        failed()
        print(e)
        exit(1)

    printm("Testing event sender ({0})".format(config.tcell_input_url))
    from tcell_agent.rust.basic_checks import test_event_sender  # pylint: disable=no-name-in-module
    from tcell_agent.events.server_agent_details import ServerAgentDetailsEvent  # pylint: disable=no-name-in-module
    from tcell_agent.version import VERSION  # pylint: disable=no-name-in-module
    try:
        errors = test_event_sender(config,
                                   [ServerAgentDetailsEvent()],
                                   VERSION,
                                   str(uuid.uuid4()))
        if errors:
            failed()
            exit(1)
            map(print, errors)
        else:
            passed()
    except Exception as e:
        failed()
        print(e)
        exit(1)


def run(_parser, args):
    import tcell_agent  # pylint: disable=import-self

    executable_with_path = which(args.command)
    if executable_with_path is None:
        print("command '", args.command, "' not found.")
        sys.exit(1)

    executable = os.path.basename(executable_with_path)

    path = os.path.dirname(tcell_agent.__file__)
    tcellpath = os.path.realpath(path + "/../tcell_agent/pythonpath")
    old_pythonpath = os.environ.get('PYTHONPATH', None)
    if old_pythonpath is not None:
        pythonpath_parts = old_pythonpath.split(":")
        if len(pythonpath_parts) > 0:
            first_package = pythonpath_parts.pop()
            if "newrelic" in first_package:
                pythonpath = first_package + ":" + tcellpath
                if len(pythonpath_parts) > 0:
                    pythonpath += ":" + ":".join(pythonpath_parts)
            else:
                pythonpath = tcellpath + ":" + old_pythonpath

        else:
            pythonpath = tcellpath + ":" + old_pythonpath

    else:
        pythonpath = tcellpath

    os.environ['PYTHONPATH'] = pythonpath
    if args.config is not None:
        os.environ['TCELL_AGENT_CONFIG'] = args.config
    if args.home is not None:
        os.environ['TCELL_AGENT_HOME'] = args.home

    new_args = args.argument
    new_args.append(os.environ)
    os.execle(executable_with_path, executable, *new_args)


class _HelpAction(argparse._HelpAction):
    def __call__(self, parser, namespace, values, option_string=None):
        parser.print_usage()
        print('''
Common commands:
 run <application command>     Run an application with tcell_agent instrumentation
 setup                         Can be used for initial setup if you do not download config from the UI
 loglevel <level>              Set the logging level or turn it off.
 test                          Test that the config and network settings are valid
 demomode                      Enable or disable mode in config file
 packages                      Report all installed packages
''')
        # retrieve subparsers from parser
        subparsers_actions = [
            action for action in parser._actions
            if isinstance(action, argparse._SubParsersAction)]
        # there will probably only be one subparser_action,
        # but better save than sorry
        for subparsers_action in subparsers_actions:
            # get all subparsers and print help
            for _choice, subparser in subparsers_action.choices.items():
                subparser.print_usage()
                # print(subparser.format_help())
        print()
        parser.exit()


class TCellAgentMain(object):
    def __init__(self):
        from tcell_agent import version  # pylint: disable=no-name-in-module

        parser = argparse.ArgumentParser(description='tCell.io Agent')
        parser = argparse.ArgumentParser(add_help=False)  # here we turn off default help action
        parser.add_argument('-h', '--help', action=_HelpAction,
                            help='help for help if you need some help')  # add custom help
        parser.add_argument('--version', action='version', version='%(prog)s ' + version.VERSION)
        # parser = argparse.ArgumentParser(
        #    description='tCell.io Agent',
        #    usage='%s' % (sys.argv[0]),
        #    add_help=False)

        subparsers = parser.add_subparsers(dest='command', metavar='{run,setup,test,loglevel,packages}')

        run_command = subparsers.add_parser('run')
        run_command.add_argument('command', help='Run an application with tcell_agent instrumentation')
        run_command.add_argument('argument', nargs=argparse.REMAINDER, help='Run an application with tcell_agent instrumentation')
        run_command.add_argument('-c', '--config', help='Location of initial tCell configuration file')
        run_command.add_argument('--home', help='tCell home (if not CWD) where logs & cache files will be placed')
        run_command.set_defaults(func=run)

        setup_command = subparsers.add_parser('setup')
        setup_command.set_defaults(func=setup)

        test_command = subparsers.add_parser('test')
        test_command.set_defaults(func=test)

        loglevel_choices = ["debug", "info", "warn", "error", "off"]
        loglevel_command = subparsers.add_parser('loglevel')
        loglevel_command.add_argument('level', help='Log Level (' + ",".join(loglevel_choices) + ")",
                                      choices=loglevel_choices)
        loglevel_command.set_defaults(func=loglevel)

        demo_command = subparsers.add_parser('demomode')
        demo_command.set_defaults(func=demomode)
        demo_command.add_argument('-o', '--off', action='store_true')

        packages_command = subparsers.add_parser('packages')
        packages_command.set_defaults(func=report_packages)

        args = parser.parse_args()
        args.func(parser, args)


if __name__ == "__main__":
    TCellAgentMain()
