import json
import logging
import urllib.request

import boto3

SUCCESS = "SUCCESS"
FAILED = "FAILED"

# Configure logging
LOGGER = logging.getLogger(__name__)
LOGGER.setLevel(logging.DEBUG)

client = boto3.client('codedeploy')


# Lambda Handler
def handler(event, context):
    LOGGER.info("Received event: " + json.dumps(event, indent=2))

    request_type = event['RequestType']

    if request_type == 'Create':
        create_deployment_group(event, context)
    elif request_type == 'Update':
        update_deployment_group(event, context)
    elif request_type == 'Delete':
        delete_deployment_group(event, context)


def create_deployment_group(event, context):
    data = {}
    status = FAILED
    deployment_group_name_ = None
    try:
        application_name_ = event['ResourceProperties']['ApplicationName']
        deployment_group_name_ = event['ResourceProperties']['DeploymentGroupName']
        deployment_config_name_ = event['ResourceProperties']['DeploymentConfigName']
        service_role_arn_ = event['ResourceProperties']['ServiceRoleArn']
        blue_target_group_ = event['ResourceProperties']['BlueTargetGroup']
        green_target_group_ = event['ResourceProperties']['GreenTargetGroup']
        prod_listener_arn_ = event['ResourceProperties']['ProdListenerArn']
        test_listener_arn_ = event['ResourceProperties']['TestListenerArn']
        cluster_name_ = event['ResourceProperties']['EcsClusterName']
        service_name_ = event['ResourceProperties']['EcsServiceName']
        termination_wait_time = event['ResourceProperties']['TerminationWaitTime']

        client.create_deployment_group(
            applicationName=application_name_,
            deploymentGroupName=deployment_group_name_,
            deploymentConfigName=deployment_config_name_,
            serviceRoleArn=service_role_arn_,
            deploymentStyle={
                'deploymentType': 'BLUE_GREEN',
                'deploymentOption': 'WITH_TRAFFIC_CONTROL'
            },
            blueGreenDeploymentConfiguration={
                'terminateBlueInstancesOnDeploymentSuccess': {
                    'action': 'TERMINATE',
                    'terminationWaitTimeInMinutes': int(termination_wait_time)
                },
                'deploymentReadyOption': {
                    'actionOnTimeout': 'CONTINUE_DEPLOYMENT'
                }
            },
            autoRollbackConfiguration={
                'enabled': True,
                'events': [
                    'DEPLOYMENT_FAILURE',
                    'DEPLOYMENT_STOP_ON_REQUEST',
                    'DEPLOYMENT_STOP_ON_ALARM'
                ]
            },
            ecsServices=[
                {
                    'serviceName': service_name_,
                    'clusterName': cluster_name_
                },
            ],
            loadBalancerInfo={
                'targetGroupPairInfoList': [
                    {
                        'targetGroups': [
                            {
                                'name': blue_target_group_
                            },
                            {
                                'name': green_target_group_
                            }
                        ],
                        'prodTrafficRoute': {
                            'listenerArns': [
                                prod_listener_arn_
                            ]
                        },
                        'testTrafficRoute': {
                            'listenerArns': [
                                test_listener_arn_
                            ]
                        }
                    },
                ]
            }
        )
        data = {
            "event": "Resource created",
            "deploymentGroupName": deployment_group_name_
        }
        status = SUCCESS
    except BaseException as e:
        raise e
    finally:
        send(event=event,
             context=context,
             physical_resource_id=deployment_group_name_,
             response_status=status,
             response_data=data)


def update_deployment_group(event, context):
    data = {}
    status = FAILED
    new_deployment_group_name_ = None
    try:
        application_name_ = event['ResourceProperties']['ApplicationName']
        current_deployment_group_name_ = event['OldResourceProperties']['DeploymentGroupName']
        new_deployment_group_name_ = event['ResourceProperties']['DeploymentGroupName']
        deployment_config_name_ = event['ResourceProperties']['DeploymentConfigName']
        service_role_arn_ = event['ResourceProperties']['ServiceRoleArn']
        blue_target_group_ = event['ResourceProperties']['BlueTargetGroup']
        green_target_group_ = event['ResourceProperties']['GreenTargetGroup']
        prod_listener_arn_ = event['ResourceProperties']['ProdListenerArn']
        test_listener_arn_ = event['ResourceProperties']['TestListenerArn']
        cluster_name_ = event['ResourceProperties']['EcsClusterName']
        service_name_ = event['ResourceProperties']['EcsServiceName']
        termination_wait_time = event['ResourceProperties']['TerminationWaitTime']

        client.update_deployment_group(
            applicationName=application_name_,
            currentDeploymentGroupName=current_deployment_group_name_,
            newDeploymentGroupName=new_deployment_group_name_,
            deploymentConfigName=deployment_config_name_,
            serviceRoleArn=service_role_arn_,
            deploymentStyle={
                'deploymentType': 'BLUE_GREEN',
                'deploymentOption': 'WITH_TRAFFIC_CONTROL'
            },
            blueGreenDeploymentConfiguration={
                'terminateBlueInstancesOnDeploymentSuccess': {
                    'action': 'TERMINATE',
                    'terminationWaitTimeInMinutes': int(termination_wait_time)
                },
                'deploymentReadyOption': {
                    'actionOnTimeout': 'CONTINUE_DEPLOYMENT'
                }
            },
            autoRollbackConfiguration={
                'enabled': True,
                'events': [
                    'DEPLOYMENT_FAILURE',
                    'DEPLOYMENT_STOP_ON_REQUEST',
                    'DEPLOYMENT_STOP_ON_ALARM'
                ]
            },
            ecsServices=[
                {
                    'serviceName': service_name_,
                    'clusterName': cluster_name_
                },
            ],
            loadBalancerInfo={
                'targetGroupPairInfoList': [
                    {
                        'targetGroups': [
                            {
                                'name': blue_target_group_
                            },
                            {
                                'name': green_target_group_
                            }
                        ],
                        'prodTrafficRoute': {
                            'listenerArns': [
                                prod_listener_arn_
                            ]
                        },
                        'testTrafficRoute': {
                            'listenerArns': [
                                test_listener_arn_
                            ]
                        }
                    },
                ]
            }
        )
        data = {
            "event": "Resource updated",
            "deploymentGroupName": new_deployment_group_name_
        }
        status = SUCCESS
    except BaseException as e:
        raise e
    finally:
        send(event=event,
             context=context,
             physical_resource_id=new_deployment_group_name_,
             response_status=status,
             response_data=data)


def delete_deployment_group(event, context):
    data = {}
    status = FAILED
    deployment_group_name_ = None
    try:
        application_name_ = event['ResourceProperties']['ApplicationName']
        deployment_group_name_ = event['ResourceProperties']['DeploymentGroupName']

        client.delete_deployment_group(
            applicationName=application_name_,
            deploymentGroupName=deployment_group_name_
        )
        status = SUCCESS

        data = {
            "event": "Resource deleted",
            "deploymentGroupName": deployment_group_name_
        }
    except BaseException as e:
        raise e
    finally:
        send(event=event,
             context=context,
             physical_resource_id=deployment_group_name_,
             response_status=status,
             response_data=data)


def send(event, context, response_status, response_data, physical_resource_id=None, no_echo=False):
    response_url = event['ResponseURL']

    LOGGER.info(response_url)

    response_body = {
        'Status': response_status,
        'Reason': 'See the details in CloudWatch Log Stream: ' + context.log_stream_name,
        'PhysicalResourceId': physical_resource_id or context.log_stream_name, 'StackId': event['StackId'],
        'RequestId': event['RequestId'], 'LogicalResourceId': event['LogicalResourceId'],
        'NoEcho': no_echo,
        'Data': response_data
    }

    json_response_body = json.dumps(response_body)

    LOGGER.info("Response body:\n" + json_response_body)

    headers = {
        'content-type': '',
        'content-length': str(len(json_response_body))
    }

    try:
        req = urllib.request.Request(response_url,
                                     data=json_response_body.encode('utf-8'),
                                     headers=headers,
                                     method='PUT')
        response = urllib.request.urlopen(req)
        LOGGER.info("Status code: " + response.reason)
    except Exception as e:
        LOGGER.error("send(..) failed executing requests.put(..): " + str(e))
