#!/usr/bin/env python

import os
import sys

from optparse import OptionParser

import ansigenome.constants as c
import ansigenome.ui as ui
import ansigenome.test_helpers as th
import ansigenome.utils as utils

from ansigenome.config import Config
from ansigenome.init import Init
from ansigenome.reqs import Reqs
from ansigenome.run import Run
from ansigenome.scan import Scan


def get_action(args):
    """
    Get the action the user wants to execute from the
    sys argv list.
    """
    for i in range(0, len(args)):
        arg = args[i]
        if arg in c.VALID_ACTIONS:
            del args[i]
            return arg
    return None


def get_opt(options, k, defval=""):
    """
    Returns an option from an Optparse values instance.
    """
    try:
        data = getattr(options, k)
    except:
        return defval
    if k == "roles_path":
        if os.pathsep in data:
            data = data.split(os.pathsep)[0]
    return data


def build_option_parser(action):
    """
    Builds an option parser object based on the action
    the user wants to execute.
    """
    usage = ui.usage()
    epilog = ui.epilogue(os.path.basename(sys.argv[0]))

    OptionParser.format_epilog = lambda self, formatter: self.epilog
    parser = OptionParser(usage=usage, epilog=epilog)

    if not action:
        parser.print_help()
        sys.exit()

    usage_prefix = "Usage: %%prog %s" % action

    if action == "config":
        parser.set_usage("{0} [-o OUT_FILE]".format(usage_prefix))
    elif action == "scan":
        parser.set_usage("{0} ROLES_PATH".format(usage_prefix))
    elif action == "gendoc":
        parser.set_usage("{0} ROLES_PATH ".format(usage_prefix))
    elif action == "genmeta":
        parser.set_usage("{0} ROLES_PATH ".format(usage_prefix))
    elif action == "reqs":
        parser.set_usage("{0} ROLES_PATH [-o OUT_FILE] [-f FORMAT]"
                         "[-v INTERACTIVE_VERSION]".format(usage_prefix))
        parser.add_option("-f", "--format",
                          dest="format",
                          help="output format, overrides the config value")
        parser.add_option("-v", "--interactive-version",
                          dest="interactive_version",
                          action="store_true",
                          help="input the version for each role 1 by 1")
    elif action == "init":
        parser.set_usage("{0} ROLE_PATH "
                         "[-c GALAXY_CATEGORIES]".format(usage_prefix))
        parser.add_option("-c", "--galaxy-categories",
                          dest="galaxy_categories",
                          help="comma separated string of galaxy categories")
    elif action == "run":
        parser.set_usage("{0} ROLES_PATH -m COMMAND".format
                         (usage_prefix))
        parser.add_option("-m", "--command", dest="command",
                          help="execute this shell command on each role")
    elif action == "dump":
        parser.set_usage("{0} ROLES_PATH -o OUT_FILE ".format(usage_prefix))
    if action in ("scan", "gendoc", "genmeta", "run", "dump"):
        parser.add_option("-l", "--limit", dest="limit",
                          help="comma separated string of roles to white list")
    if action in ("config", "reqs", "dump"):
        parser.add_option("-o", "--out", dest="out_file",
                          help="output file path")

    return parser


def execute_config(args, options, config, parser):
    """
    Execute the config action.
    """
    Config(args, options, config)


def execute_scan(args, options, config, parser):
    """
    Execute the scan action.
    """
    check_roles_path(args, parser)
    Scan(args, options, config)


def execute_gendoc(args, options, config, parser):
    """
    Execute the gendoc action.
    """
    check_roles_path(args, parser)
    Scan(args, options, config, gendoc=True)


def execute_genmeta(args, options, config, parser):
    """
    Execute the genmeta action.
    """
    check_roles_path(args, parser)
    Scan(args, options, config, genmeta=True)


def execute_run(args, options, config, parser):
    """
    Execute the run action.
    """
    if options.command is None:
        parser.print_help()
        sys.exit()

    check_roles_path(args, parser)
    Run(args, options, config)


def execute_init(args, options, config, parser):
    """
    Execute the init action.
    """
    if len(args) == 0:
        parser.print_help()
        sys.exit()

    Init(args, options, config)


def execute_reqs(args, options, config, parser):
    """
    Execute the reqs action.
    """
    check_roles_path(args, parser)
    Reqs(args, options, config)


def execute_dump(args, options, config, parser):
    """
    Execute the dump action.
    """
    if options.out_file is None:
        parser.print_help()
        sys.exit()

    check_roles_path(args, parser)
    Scan(args, options, config, dump=True)


def check_roles_path(args, parser):
    """
    Use the default role path or the user supplied roles path.
    """
    found_path = False

    if len(args) == 0:
        roles_path = os.path.join(os.getcwd(), "playbooks", "roles")

        if os.path.exists(roles_path):
            found_path = True

        if not found_path:
            roles_path = os.getcwd()
            found_path = True
    else:
        roles_path = args[0]

    if not os.path.exists(roles_path):
        ui.error(c.MESSAGES["path_missing"], roles_path)
        sys.exit(1)

    if len(args) == 0:
        # there are no args, so add it at as the first arg
        args.append(roles_path)
    else:
        # replace the old argument with the new one
        args[0] = roles_path


def load_config():
    """
    Load properties from the yaml file.
    """
    default_config_path = os.path.join(c.CONFIG_DEFAULT_PATH, c.CONFIG_FILE)
    pwd_config_path = os.path.join(os.getcwd(), c.CONFIG_FILE)
    found_config = False

    if os.path.exists(pwd_config_path):
        this_config = pwd_config_path
        found_config = True
    elif not found_config and os.path.exists(default_config_path):
        this_config = default_config_path
    else:
        # There are no configs at all, so let's help the user make one.
        Config([], {}, {})
        sys.exit(0)

    return (utils.yaml_load(this_config, err_quit=True), this_config)


def load_test_config():
    """
    Load the test config when running tests.
    """
    test_path = os.path.join(c.TEST_PATH, c.CONFIG_FILE)
    th.create_ansigenome_config(test_path)

    return (utils.yaml_load(test_path, err_quit=True), test_path)


def validate_config(config, path):
    """
    Enforce that certain keys exist in the config.
    """
    config_keys = utils.keys_in_dict(config, "", [])
    default_config_keys = utils.keys_in_dict(c.CONFIG_MUST_CONTAIN, "", [])

    for key in default_config_keys:
        if key not in config_keys:
            ui.error(c.MESSAGES["invalid_config"].replace("%key", key),
                     path)
            sys.exit(1)


def main():
    """
    Main entry point to the application.
    """
    action = get_action(sys.argv)
    parser = build_option_parser(action)
    (options, args) = parser.parse_args()

    # A little bit hacky but this lets us use an isolated test config.
    if os.getenv("TEST"):
        (config, config_path) = load_test_config()
    else:
        (config, config_path) = load_config()

    # skip validating the config when generating a config
    if action != "config":
        validate_config(config, config_path)

    args = utils.stripped_args(args)

    if 1:
        fn = globals()["execute_%s" % action]
        fn(args, options, config, parser)


if __name__ == "__main__":
    main()
