from contextlib import contextmanager
from tests.system.tools import waituntil
import logging
log = logging.getLogger(__name__)


@contextmanager
def prepare_bootstrap(manifest, build_server):
    if manifest.volume['backing'] == 's3':
        credentials = {'access-key': build_server.build_settings['ec2-credentials']['access-key'],
                       'secret-key': build_server.build_settings['ec2-credentials']['secret-key']}
        from boto.s3 import connect_to_region as s3_connect
        s3_connection = s3_connect(manifest.image['region'],
                                   aws_access_key_id=credentials['access-key'],
                                   aws_secret_access_key=credentials['secret-key'])
        log.debug('Creating S3 bucket')
        bucket = s3_connection.create_bucket(manifest.image['bucket'], location=manifest.image['region'])
        try:
            yield
        finally:
            log.debug('Deleting S3 bucket')
            for item in bucket.list():
                bucket.delete_key(item.key)
            s3_connection.delete_bucket(manifest.image['bucket'])
    else:
        yield


@contextmanager
def boot_image(manifest, build_server, bootstrap_info, instance_type=None):

    credentials = {'access-key': build_server.run_settings['ec2-credentials']['access-key'],
                   'secret-key': build_server.run_settings['ec2-credentials']['secret-key']}
    from boto.ec2 import connect_to_region as ec2_connect
    ec2_connection = ec2_connect(bootstrap_info._ec2['region'],
                                 aws_access_key_id=credentials['access-key'],
                                 aws_secret_access_key=credentials['secret-key'])
    from boto.vpc import connect_to_region as vpc_connect
    vpc_connection = vpc_connect(bootstrap_info._ec2['region'],
                                 aws_access_key_id=credentials['access-key'],
                                 aws_secret_access_key=credentials['secret-key'])

    if manifest.volume['backing'] == 'ebs':
        from .images import EBSImage
        image = EBSImage(bootstrap_info._ec2['image'], ec2_connection)
    if manifest.volume['backing'] == 's3':
        from .images import S3Image
        image = S3Image(bootstrap_info._ec2['image'], ec2_connection)

    try:
        with run_instance(image, manifest, instance_type, ec2_connection, vpc_connection) as instance:
            yield instance
    finally:
        image.destroy()


@contextmanager
def run_instance(image, manifest, instance_type, ec2_connection, vpc_connection):

    with create_env(ec2_connection, vpc_connection) as boot_env:

        def waituntil_instance_is(state):
            def instance_has_state():
                instance.update()
                return instance.state == state
            return waituntil(instance_has_state, timeout=600, interval=3)

        instance = None
        try:
            log.debug('Booting ec2 instance')
            reservation = image.ami.run(instance_type=instance_type,
                                        subnet_id=boot_env['subnet_id'])
            [instance] = reservation.instances
            instance.add_tag('Name', 'bootstrap-vz test instance')

            if not waituntil_instance_is('running'):
                raise EC2InstanceStartupException('Timeout while booting instance')

            if not waituntil(lambda: instance.get_console_output().output is not None, timeout=600, interval=3):
                raise EC2InstanceStartupException('Timeout while fetching console output')

            from bootstrapvz.common.releases import wheezy
            if manifest.release <= wheezy:
                termination_string = 'INIT: Entering runlevel: 2'
            else:
                termination_string = 'Debian GNU/Linux'

            console_output = instance.get_console_output().output
            if termination_string not in console_output:
                last_lines = '\n'.join(console_output.split('\n')[-50:])
                message = ('The instance did not boot properly.\n'
                           'Last 50 lines of console output:\n{output}'.format(output=last_lines))
                raise EC2InstanceStartupException(message)

            yield instance
        finally:
            if instance is not None:
                log.debug('Terminating ec2 instance')
                instance.terminate()
                if not waituntil_instance_is('terminated'):
                    raise EC2InstanceStartupException('Timeout while terminating instance')
                # wait a little longer, aws can be a little slow sometimes and think the instance is still running
                import time
                time.sleep(15)


@contextmanager
def create_env(ec2_connection, vpc_connection):

    vpc_cidr = '10.0.0.0/28'
    subnet_cidr = '10.0.0.0/28'

    @contextmanager
    def vpc():
        log.debug('Creating VPC')
        vpc = vpc_connection.create_vpc(vpc_cidr)
        try:
            yield vpc
        finally:
            log.debug('Deleting VPC')
            vpc_connection.delete_vpc(vpc.id)

    @contextmanager
    def subnet(vpc):
        log.debug('Creating subnet')
        subnet = vpc_connection.create_subnet(vpc.id, subnet_cidr)
        try:
            yield subnet
        finally:
            log.debug('Deleting subnet')
            vpc_connection.delete_subnet(subnet.id)

    with vpc() as _vpc:
        with subnet(_vpc) as _subnet:
            yield {'subnet_id': _subnet.id}


class EC2InstanceStartupException(Exception):
    pass
