#!python

import yaml
import shutil
import argparse
import requests
import functools
import traceback
import logging
from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.schedulers.background import BackgroundScheduler
import hallmonitor.services.endpoint as endpoint_service
import hallmonitor.addons as addons
from colorama import Fore, Back, Style

logging.basicConfig(level=logging.DEBUG)

# Use globals to inject headers. Follow the example for Auth0 header
global AUTH_HEADER
AUTH_HEADER=None

# Define custom actions up here
def export_auth_header(header):
    global AUTH_HEADER
    AUTH_HEADER=header

ACTION_MAPPING = {
    'auth_header': export_auth_header
}

# Define custom addons here
ADDON_MAPPING = {
    'auth0': addons.auth0_handler
}

def resolve_addon(addon):
    """
    Look I know this is evil but we parse the yaml and do what it says
    to resolve addon functionality. Someday will have a better way
    """
    addon_name = addon['name']
    addon_handler = ADDON_MAPPING[addon_name]
    addon_action = addon['action']
    result = addon_handler(addon)
    action = ACTION_MAPPING[addon_action]
    action(result)


def save_response(filename, content):
    with open(filename, 'wb') as f:
        f.write(content)

def handle_test():
    """
    Decorator for graceful connection handling
    """
    def _decorator(run_function):
        @functools.wraps(run_function)
        def _caller(*args, **kwargs):
            # Print header
            print(f'{Fore.WHITE}=' * shutil.get_terminal_size().columns)

            # Print test description
            print(f'{Fore.YELLOW}{kwargs.get("description")}')

            # Reset style
            print(Style.RESET_ALL)

            try:
                res = run_function(*args, **kwargs)
                if res.status_code == 200:
                    print(f'{Fore.BLUE}{res}')

                    if kwargs.get('json'):
                        print(f'{Fore.BLUE}{res.json()}')

                    if kwargs.get('outfile'):
                        save_response(kwargs.get('outfile'), res.content)
                else:
                    print(f'{Fore.RED}{res}')
                    print(f'{Fore.RED}{res.json()}')

                print(Style.RESET_ALL)
                if kwargs.get('store_results'):
                    endpoint_service.store_results(kwargs, res)

            except:
                print(f'{Fore.RED}')
                traceback.print_exc()
                print(Style.RESET_ALL)

        return _caller
    return _decorator


@handle_test()
def test_case(**kwargs):
    method = kwargs.get('method')
    method_mapping = {
        'GET': functools.partial(requests.get),
        'PUT': functools.partial(requests.put),
        'POST': functools.partial(requests.post),
        'DELETE': functools.partial(requests.delete),
    }
    request_function = method_mapping.get(method)
    if not request_function:
        raise Exception(f'{method} is not a valid HTTP request method! Try something from {method_mapping}')

    return request_function(f'{kwargs["base"]}{kwargs["route"]}', params=kwargs.get('params'), headers=AUTH_HEADER)

def main(config=None, cli=True, **kwargs):
    print(f'config {config}')
    print(f'cli {cli}')
    print(f'kwargs {kwargs}')
    if cli is True:
        # Load optional arguments if using cli. Otherwise pass args from main
        parser = argparse.ArgumentParser()
        parser.add_argument("--path", "-p", default='', help="Path to config file")
        parser.add_argument("--file", "-f", help="Name of config file", required=True)
        parser.add_argument("--name", "-n", help="Name of test to run in --file", default=None)
        parser.add_argument("--monitor", "-m", help="Turns on monitoring", action='store_true')
        parser.add_argument("--schedule", "-s", help="Minute schedule to run this on", type=float, default=0.1)
        args = parser.parse_args()
        filename = args.file
        test_name = args.name
        path = args.path
        monitor = args.monitor
        schedule = args.schedule
        scheduler = BackgroundScheduler()
    else:
        monitor = kwargs.get('monitor')
        schedule = kwargs.get('schedule', 0.1)
        scheduler = BackgroundScheduler()
        test_name = None

    if not config:
        with open(f'{path}{filename}') as stream:
            config = yaml.safe_load(stream)

    addons = config.get('addons', [])
    for addon in addons:
        resolve_addon(addon)

    test_cases = config.get('test_cases', [])
    if test_name:
        for test in test_cases:
            if test_name == test.get('name', 'No Name'):
                test_case(**test)
    else:
        for test in test_cases:
            test_case(**test)

    if monitor:
        for test in test_cases:
            test['store_results'] = True
            job = scheduler.add_job(test_case, 'interval', kwargs=test, minutes=schedule)

        scheduler.start()

if __name__ == '__main__':
    main()
