#!/usr/bin/env python3

# Copyright 2020 Spirent Communications
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""VALID main script.
"""

import logging
import os
import sys
import argparse
import time
import datetime
from conf import settings
import core.component_factory as component_factory
from core.loader import Loader

VERBOSITY_LEVELS = {
    'debug': logging.DEBUG,
    'info': logging.INFO,
    'warning': logging.WARNING,
    'error': logging.ERROR,
    'critical': logging.CRITICAL
}

_CURR_DIR = os.path.dirname(os.path.realpath(__file__))
_LOGGER = logging.getLogger()

def parse_arguments():
    """
    Parse command line arguments.
    """
    class _SplitValidationTypesAction(argparse.Action):
        """
        Parse and split '--test-params' arguments.

        This expects either a single list validation types
        e.g: --validation-type 'configuration, state'
        """
        def __call__(self, parser, namespace, values, option_string=None):
            values = values.strip()
            input_list = values.split(',')
            print(input_list)
            parameter_list = []
            for vtype in input_list:
                vtype = vtype.strip()
                if vtype:
                    vtype = vtype.lower()
                    parameter_list.append(str(vtype))
            # results = {'_PARAMS_LIST':parameter_list}
            setattr(namespace, self.dest, parameter_list)

    parser = argparse.ArgumentParser(prog=__file__, formatter_class=
                                     argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument('--version', action='version', version='%(prog)s 0.1')
    parser.add_argument('--list-validations', action='store_true',
                        help='list all validations')
    parser.add_argument('--list-vurls', action='store_true',
                        help='list all Software pre-dep Hyperlinks validations and exit')
    parser.add_argument('--list-vconfig', action='store_true',
                        help='list all Software pre-dep Configuration validations and exit')
    parser.add_argument('--list-vstate', action='store_true',
                        help='list all Software post-dep State validations and exit')
    parser.add_argument('--list-vsecurity', action='store_true',
                        help='list all Software post-dep Security validations and exit')
    parser.add_argument('--list-vnwlinks', action='store_true',
                        help='list all Network-Links validations and exit')
    parser.add_argument('--list-vresmod', action='store_true',
                        help='list all Resource-Model validations and exit')
    parser.add_argument('--validation', action=_SplitValidationTypesAction,
                        help='The type of Validation to perform - resmod, nwlinks,\
                        urls, configuration, state, security')
    args = vars(parser.parse_args())

    return args


def configure_logging(level):
    """Configure logging.
    """
    name, ext = os.path.splitext(settings.getValue('LOG_FILE_DEFAULT'))
    rename_default = "{name}_{uid}{ex}".format(name=name,
                                               uid=settings.getValue(
                                                   'LOG_TIMESTAMP'),
                                               ex=ext)
    log_file_default = os.path.join(
        settings.getValue('RESULTS_PATH'), rename_default)
    _LOGGER.setLevel(logging.DEBUG)
    stream_logger = logging.StreamHandler(sys.stdout)
    stream_logger.setLevel(VERBOSITY_LEVELS[level])
    stream_logger.setFormatter(logging.Formatter(
        '[%(levelname)-5s]  %(asctime)s : (%(name)s) - %(message)s'))
    _LOGGER.addHandler(stream_logger)
    file_logger = logging.FileHandler(filename=log_file_default)
    file_logger.setLevel(logging.DEBUG)
    file_logger.setFormatter(logging.Formatter(
        '%(asctime)s : %(message)s'))
    _LOGGER.addHandler(file_logger)

def handle_list_options(args):
    """ Process --list cli arguments if needed

    :param args: A dictionary with all CLI arguments
    """
    if args['list_vurls']:
        print(Loader().get_swpreurlsvalidators_printable())
        sys.exit(0)

    if args['list_vconfig']:
        print(Loader().get_swpreconfigvalidators_printable())
        sys.exit(0)

    if args['list_vstate']:
        print(Loader().get_swpoststatevalidators_printable())
        sys.exit(0)

    if args['list_vsecurity']:
        print(Loader().get_swpostsecurityvalidators_printable())
        sys.exit(0)

    if args['list_vnwlinks']:
        print(Loader().get_nwlinksvalidators_printable())
        sys.exit(0)


# Sflo: questions/3041986/apt-command-line-interface-like-yes-no-input
def sanity_check(question, default="yes"):
    """Ask a yes/no question via raw_input() and return their answer.

    "question" is a string that is presented to the user.
    "default" is the presumed answer if the user just hits <Enter>.
        It must be "yes" (the default), "no" or None (meaning
        an answer is required of the user).

    The "answer" return value is True for "yes" or False for "no".
    """
    valid = {"yes": True, "y": True, "ye": 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 main():
    """Main function.
    """
    args = parse_arguments()

    if not sanity_check("Have you configured the testcases ?"):
        print("Please configure testcases and rerun")
        sys.exit(1)

    print(args)

    # define the timestamp to be used by logs and results
    date = datetime.datetime.fromtimestamp(time.time())
    timestamp = date.strftime('%Y-%m-%d_%H-%M-%S')
    settings.setValue('LOG_TIMESTAMP', timestamp)


    # configure settings
    settings.load_from_dir(os.path.join(_CURR_DIR, 'conf'))

    # if required, handle list-* operations
    handle_list_options(args)

    results_dir = "results_" + timestamp
    results_path = os.path.join(settings.getValue('LOG_DIR'), results_dir)
    settings.setValue('RESULTS_PATH', results_path)
    # create results directory
    if not os.path.exists(results_path):
        os.makedirs(results_path)

    configure_logging(settings.getValue('VERBOSITY'))

    loader = Loader()
    validations = settings.getValue('VALIDATIONS')

    # Get the Validation Types.
    if args['validation']:
        validations = args.validation_type

    validator_objs = []
    for validation in validations:
        if 'urls' in validation:
            validators = loader.get_swpreurlsvalidators()
            if settings.getValue('SW_PRE_URLS_VALIDATOR') not in validators:
                _LOGGER.error('There are no urls validators matching \'%s\' found in'
                              ' \'%s\'. Exiting...', settings.getValue('SW_PRE_URLS_VALIDATOR'),
                              settings.getValue('SW_PRE_URLS_VALID_DIR'))
                sys.exit(1)
            validator_ctrl = component_factory.create_swpreurlsvalidator(
                loader.get_swpreurlsvalidator_class())
            validator_objs.append(validator_ctrl)
        if 'configuration' in validation:
            validators = loader.get_swpreconfigvalidators()
            if settings.getValue('SW_PRE_CONFIG_VALIDATOR') not in validators:
                _LOGGER.error('There are no configvalidators matching \'%s\' found in'
                              ' \'%s\'. Exiting...', settings.getValue('SW_PRE_CONFIG_VALIDATOR'),
                              settings.getValue('SW_PRE_CONFIG_VALID_DIR'))
                sys.exit(1)
            validator_ctrl = component_factory.create_swpreconfigvalidator(
                loader.get_swpreconfigvalidator_class())
            validator_objs.append(validator_ctrl)
        if 'state' in validation:
            validators = loader.get_swpoststatevalidators()
            if settings.getValue('SW_POST_STATE_VALIDATOR') not in validators:
                _LOGGER.error('There are no statevalidators matching \'%s\' found in'
                              ' \'%s\'. Exiting...', settings.getValue('SW_POST_STATE_VALIDATOR'),
                              settings.getValue('SW_POST_STATE_VALID_DIR'))
                sys.exit(1)
            validator_ctrl = component_factory.create_swpoststatevalidator(
                loader.get_swpoststatevalidator_class())
            validator_objs.append(validator_ctrl)
        if 'security' in validation:
            validators = loader.get_swpostsecurityvalidators()
            if settings.getValue('SW_POST_SECURITY_VALIDATOR') not in validators:
                _LOGGER.error('There are no securityvalidators matching \'%s\' found in'
                              ' \'%s\'. Exiting...', settings.getValue('SW_POST_SECURITY_VALIDATOR'),
                              settings.getValue('SW_POST_SECURITY_VALID_DIR'))
                sys.exit(1)
            validator_ctrl = component_factory.create_swpostsecurityvalidator(
                loader.get_swpostsecurityvalidator_class())
            validator_objs.append(validator_ctrl)
        if 'nwlinks' in validation:
            validators = loader.get_nwlinksvalidators()
            if settings.getValue('NW_LINKS_VALIDATOR') not in validators:
                _LOGGER.error('There are no nwlinksvalidators matching \'%s\' found in'
                              ' \'%s\'. Exiting...', settings.getValue('NW_LINKS_VALIDATOR'),
                              settings.getValue('NW_LINKS_VALID_DIR'))
                sys.exit(1)
            validator_ctrl = component_factory.create_nwlinksvalidator(
                loader.get_nwlinksvalidator_class())
            validator_objs.append(validator_ctrl)
        if 'resmod' in validation:
            validators = loader.get_resmodvalidators()
            if settings.getValue('RES_MOD_VALIDATOR') not in validators:
                _LOGGER.error('There are no resmodvalidators matching \'%s\' found in'
                              ' \'%s\'. Exiting...', settings.getValue('RES_MOD_VALIDATOR'),
                              settings.getValue('RES_MOD_VALID_DIR'))
                sys.exit(1)
            validator_ctrl = component_factory.create_resmodvalidator(
                loader.get_resmodvalidator_class())
            validator_objs.append(validator_ctrl)


if __name__ == "__main__":
    main()