#!/usr/bin/env python

import argparse
import sys
import time
from ast import literal_eval

try:
    import boto
    import boto.cloudformation
except ImportError:
    print("boto 2.8.0+ is required")
    sys.exit(1)

from troposphere.utils import tail


def upload_template_to_s3(conn, region, bucket_name, key_name, template):
    cloudformation_bucket = conn.lookup(bucket_name)
    if not cloudformation_bucket:
        if region == 'us-east-1':
            region = boto.s3.connection.Location.DEFAULT
        elif region == 'eu-west-1':
            region = boto.s3.connection.Location.EU

        cloudformation_bucket = conn.create_bucket(bucket_name,
                                                   location=region)
    key = cloudformation_bucket.new_key(key_name)
    key.set_contents_from_string(template)
    cfn_template_url = "https://s3.amazonaws.com/%s/%s" % (
        bucket_name, key_name)
    return cfn_template_url


def validate_template(conn, template=None, url=None):
    """Validate the template"""
    print("Validating Template")
    try:
        if url:
            conn.validate_template(template_url=url)
        elif template:
            conn.validate_template(template_body=template)
        else:
            print("Error: Must provide a template or template url")
            print("Exiting...")
            sys.exit(1)
    except boto.exception.BotoServerError as e:
        # XXX - need to figure out why this isn't getting parsed from boto.
        print("Error: %s" %
              (literal_eval(e.error_message)['Error']['Message'],))
        print("Exiting...")
        sys.exit(1)
    print("Template Validated")


def create_stack(
    conn,
    stackname,
    template=None,
    url=None,
    params=None,
    capabilities=None,
    update=False,
):
    kwargs = dict(
        template_body=template,
        template_url=url,
        parameters=params,
        capabilities=capabilities,
    )
    validate_template(conn, template=template, url=url)

    try:
        if update:
            stack_id = conn.update_stack(stackname, **kwargs)
            action = 'Updated'
        else:
            stack_id = conn.create_stack(stackname, **kwargs)
            action = 'Created'
        print("%s stack %s: %s" % (action, stackname, stack_id))
    except boto.exception.BotoServerError as e:
        # XXX - need to figure out why this isn't getting parsed from boto.
        print("Error: %s" %
              (literal_eval(e.error_message)['Error']['Message'],))
        print("Exiting...")
        sys.exit(1)


def build_s3_name(stack_name):
    timestamp = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
    name = stack_name
    if stack_name.endswith('.json'):
        name = stack_name[:-5]
    return '%s-%s.json' % (name, timestamp)


def describe_resources(conn, stackname):
    if stackname is not None:
        print(conn.describe_stack_resources(stackname))
    else:
        stacks = conn.describe_stacks()
        for stack in stacks:
            print("Resources for stack {0}:".format(stack.stack_name))
            print(conn.describe_stack_resources(stack.stack_name))


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("-c", "--create", help="Create stack using template")
    parser.add_argument("-u", "--update", action='store_true',
                        help="Update stack instead of creating")
    parser.add_argument("-b", "--bucket", dest="s3bucket",
                        help="Upload template to S3 bucket")
    parser.add_argument("-d", "--debug", action='store_true',
                        help="Turn on boto debug logging")
    parser.add_argument("-n", "--name", dest="s3name",
                        help="Template name in S3 bucket")
    parser.add_argument("-p", "--parameter", dest="params", action='append',
                        help="stack parameters in key=value form")
    parser.add_argument("-r", "--region", default="us-east-1",
                        help="Region (default %(default)s)")
    parser.add_argument("-s", "--s3-region", default=None,
                        dest="s3region",
                        help="S3 Region (default us-east-1)")
    parser.add_argument("-R", "--resources", action='store_true',
                        help="describe stack resources, list resources for "
                             "all stacks if no stack is specified")
    parser.add_argument("-t", "--tail", action='store_true',
                        help="tail event log")
    parser.add_argument(
        "-C",
        "--capability",
        dest="capabilities",
        action='append',
        help="Capability to allow in the stack",
    )
    parser.add_argument("stack", nargs='?')
    values = parser.parse_args()

    if values.params:
        values.params = [x.split('=') for x in values.params]
    else:
        values.params = []

    if values.debug:
        import logging
        logging.basicConfig(filename="boto.log", level=logging.DEBUG)

    if not values.s3region:
        values.s3region = values.region

    conn = boto.cloudformation.connect_to_region(values.region)

    if values.create:
        # Read in the template file
        template = open(values.create).read()

        # If needed, build an S3 name (key)
        if values.s3bucket and not values.s3name:
            values.s3name = build_s3_name(values.create)

        kwargs = dict(
            template=template,
            params=values.params,
            capabilities=values.capabilities,
            update=values.update,
        )

        if values.s3bucket:
            # Upload to S3 and create the stack
            s3conn = boto.s3.connect_to_region(values.s3region)
            url = upload_template_to_s3(
                s3conn, values.region, values.s3bucket,
                values.s3name, template)
            kwargs['url'] = url
            del kwargs['template']

        create_stack(conn, values.stack, **kwargs)

    if values.resources:
        describe_resources(conn, values.stack)

    if values.tail:
        tail(conn, values.stack)
