#!/usr/bin/env python3

from dispatch.core import Reactor, Plan
from dispatch.utils import import_obj
import argparse
import logging
import json
import sys

import logging

logger = logging.getLogger('dispatcher')


def configure_logger(target, level):
    root_logger = logging.getLogger()
    root_logger.setLevel(getattr(logging, level))
    formatter = logging.Formatter(
        fmt='%(asctime)s - %(levelname)s - %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S',
    )
    if target == '-':
        handler = logging.StreamHandler()
        handler.setFormatter(formatter)
    else:
        handler = logging.FileHandler(target)
        handler.setFormatter(formatter)

    root_logger.addHandler(handler)

def cli():
    parser = argparse.ArgumentParser(prog='dispatcher', add_help=True)
    parser.add_argument(
        '-d', '--debug',
        action='store_true',
        default=False,
        help='Instead of staring the reactor, print the debug plan',
    )
    parser.add_argument(
        'config',
        help='Config file (json or yaml format; yaml requires pyyaml package)',
    )
    parser.add_argument(
        '--log',
        help='Log file (- for stdout by default)',
        default='-',
    )
    parser.add_argument(
        '--log-level',
        help='Logging verbosity level',
        choices=('DEBUG', 'INFO', 'WARN'),
        default='INFO',
    )
    args = parser.parse_args(sys.argv[1:])

    configure_logger(args.log, args.log_level)
    path = args.config
    with open(path, 'r') as f:
        config = f.read()
    if path.endswith('.yaml') or path.endswith('.yml'):
        import yaml
        data = yaml.safe_load(config)
    else:
        data = json.loads(config)


    reactor = Reactor()
    if type(data) == dict:
        plans = []
        for key, value in data.items():
            plan = value
            plan['name'] = key
            plans.append(plan)
    elif type(data) == list:
        plans = data
    else:
        raise ValueError('Configuration must have an array or object at the top level')

    for plan in plans:
        name = plan.get('name', None)
        background = plan.get('background', True)
        schedule_type = plan.get('schedule_type', None)
        schedule_args = plan.get('schedule', None)
        action_path = plan.get('action')
        func = import_obj(action_path)

        if background:
            action = reactor.background_action(func)
        else:
            action = reactor.action(func)

        if type(schedule_args) != dict:
            raise ValueError('schedule_args must be an object')
        if schedule_type == 'calendar':
            schedule = reactor.schedule_calendar(**schedule_args)
        elif schedule_type == 'interval':
            schedule = reactor.schedule_interval(**schedule_args)
        else:
            raise ValueError('schedule_type must be either calendar or interval')

        plan = Plan(schedule, action, name=name)
        reactor.plans.append(plan)
        logger.info('Loaded plan={} action={} with schedule_type={} options={}'.format(
            plan, action, schedule_type, json.dumps(schedule_args)
        ))
    logger.info('Loaded {} plans'.format(len(reactor.plans)))

    if args.debug:
        reactor._debug()
    else:
        reactor.run()

if __name__ == '__main__':
    cli()
