#!/usr/bin/env python
""" Launches AWS Cloudformation stacks from config.
"""

import argparse
import logging
from collections import Mapping
import copy

import yaml
from stacker.builder import Builder

logger = logging.getLogger()

DEBUG_FORMAT = ('[%(asctime)s] %(levelname)s %(name)s:%(lineno)d'
                '(%(funcName)s) - %(message)s')
INFO_FORMAT = ('[%(asctime)s] %(message)s')

ISO_8601 = '%Y-%m-%dT%H:%M:%S'


def get_stack_definitions(config, stack_list):
    """ Extract stack definitions from the config.

    If no stack_list given, return stack config as is.
    """
    if not stack_list:
        return config['stacks']
    return [s for s in config['stacks'] if s['name'] in stack_list]


class KeyValueAction(argparse.Action):
    def __init__(self, option_strings, dest, default=None, nargs=None,
                 **kwargs):
        if nargs:
            raise ValueError("nargs not allowed")
        default = default or {}
        super(KeyValueAction, self).__init__(option_strings, dest, nargs,
                                             default=default, **kwargs)

    def __call__(self, parser, namespace, values, option_string=None):
        if not isinstance(values, Mapping):
            raise ValueError("type must be 'key_value'")
        if not getattr(namespace, self.dest):
            setattr(namespace, self.dest, {})
        getattr(namespace, self.dest).update(values)


def key_value_arg(string):
    try:
        k, v = string.split("=", 1)
    except ValueError:
        raise argparse.ArgumentTypeError(
            "%s does not match KEY=VALUE format." % string)
    return {k: v}


def parse_args():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('-r', '--region', default='us-east-1',
                        help="The AWS region to launch in. Default: "
                             "%(default)s")
    parser.add_argument('-m', '--max-zones', type=int,
                        help="Gives you the ability to limit the # of zones "
                             "that resources will be launched in. If not "
                             "given, then resources will be launched in all "
                             "available availability zones.")
    parser.add_argument('-v', '--verbose', action='count', default=0,
                        help='Increase output verbosity. May be specified up '
                             'to twice.')
    parser.add_argument('-d', '--domain',
                        help="The domain to run in. Gets converted into the "
                             "BaseDomain Parameter for use in stack "
                             "templates.")
    parser.add_argument("-p", "--parameter", dest="parameters",
                        metavar="PARAMETER=VALUE", type=key_value_arg,
                        action=KeyValueAction, default={},
                        help="Adds parameters from the command line "
                             "that can be used inside any of the stacks "
                             "being built. Can be specified more than once.")
    parser.add_argument("--stacks", action="append",
                        metavar="STACKNAME", type=str,
                        help="Only work on the stacks given. Can be "
                             "specified more than once. If not specified "
                             "then stacker will work on all stacks in the "
                             "config file.")
    parser.add_argument('config',
                        help="The config file where stack configuration is "
                             "located. Must be in yaml format.")

    return parser.parse_args()


def setup_logging(verbosity):
    log_level = logging.INFO
    log_format = INFO_FORMAT
    if verbosity > 0:
        log_level = logging.DEBUG
        log_format = DEBUG_FORMAT
    if verbosity < 2:
        logging.getLogger('boto').setLevel(logging.CRITICAL)

    return logging.basicConfig(format=log_format, datefmt=ISO_8601,
                               level=log_level)

if __name__ == '__main__':
    args = parse_args()
    setup_logging(args.verbose)
    parameters = copy.deepcopy(args.parameters)
    parameters['BaseDomain'] = args.domain

    with open(args.config) as fd:
        config = yaml.load(fd)

    builder = Builder(args.region, mappings=config['mappings'],
                      parameters=parameters)

    stack_definitions = get_stack_definitions(config, args.stacks)
    stack_names = [s['name'] for s in stack_definitions]
    logger.info("Working on stacks: %s", ', '.join(stack_names))
    builder.build(stack_definitions)
