package com.aws.pattern.infrastructure;

import java.util.Arrays;

import software.amazon.awscdk.core.CfnOutput;
import software.amazon.awscdk.core.CfnOutputProps;
import software.amazon.awscdk.core.Construct;
import software.amazon.awscdk.core.Duration;
import software.amazon.awscdk.core.Stack;
import software.amazon.awscdk.core.StackProps;
import software.amazon.awscdk.services.applicationautoscaling.EnableScalingProps;
import software.amazon.awscdk.services.ec2.ISecurityGroup;
import software.amazon.awscdk.services.ec2.Peer;
import software.amazon.awscdk.services.ec2.Port;
import software.amazon.awscdk.services.ec2.SecurityGroup;
import software.amazon.awscdk.services.ec2.SubnetSelection;
import software.amazon.awscdk.services.ec2.Vpc;
import software.amazon.awscdk.services.ecr.TagMutability;
import software.amazon.awscdk.services.ecs.AwsLogDriverProps;
import software.amazon.awscdk.services.ecs.Cluster;
import software.amazon.awscdk.services.ecs.ContainerImage;
import software.amazon.awscdk.services.ecs.CpuUtilizationScalingProps;
import software.amazon.awscdk.services.ecs.DeploymentController;
import software.amazon.awscdk.services.ecs.DeploymentControllerType;
import software.amazon.awscdk.services.ecs.LogDriver;
import software.amazon.awscdk.services.ecs.ScalableTaskCount;
import software.amazon.awscdk.services.ecs.patterns.ApplicationLoadBalancedFargateService;
import software.amazon.awscdk.services.ecs.patterns.ApplicationLoadBalancedTaskImageOptions;
import software.amazon.awscdk.services.elasticloadbalancingv2.ApplicationListener;
import software.amazon.awscdk.services.elasticloadbalancingv2.ApplicationLoadBalancer;
import software.amazon.awscdk.services.elasticloadbalancingv2.ApplicationProtocol;
import software.amazon.awscdk.services.elasticloadbalancingv2.ApplicationProtocolVersion;
import software.amazon.awscdk.services.elasticloadbalancingv2.ApplicationTargetGroup;
import software.amazon.awscdk.services.elasticloadbalancingv2.BaseApplicationListenerProps;
import software.amazon.awscdk.services.elasticloadbalancingv2.HealthCheck;
import software.amazon.awscdk.services.elasticloadbalancingv2.ListenerAction;
import software.amazon.awscdk.services.elasticloadbalancingv2.TargetType;
import software.amazon.awscdk.services.iam.ManagedPolicy;
import software.amazon.awscdk.services.iam.Role;
import software.amazon.awscdk.services.iam.RoleProps;
import software.amazon.awscdk.services.iam.ServicePrincipal;
import software.amazon.awscdk.services.logs.LogGroup;

public class EcsInfrastructureStack extends Stack {
	
	public static String ECS_CLUSTER_NAME = "BlueGreenPipelineJavaAppCluster";
	public static String ECS_SERVICE_NAME = "BlueGreenPipelineJavaAppService";
	
	public ApplicationListener greenWebListener;
	public ApplicationListener blueWebListener;
	
	public ApplicationTargetGroup greenTargetGroup;
	public ApplicationTargetGroup blueTargetGroup;

	public EcsInfrastructureStack(final Construct scope, final String id, final StackProps props) {
		super(scope, id, props);

		// *******************************************//
		// ************** VPC, Subnets****************//
		// *******************************************//

		Vpc vpc = Vpc.Builder.create(this, "MyVpcId").maxAzs(2).build();

		// *******************************************//
		// *************Security groups***************//
		// *******************************************//

		ISecurityGroup albSecurityGroup = SecurityGroup.Builder.create(this, "albSecurityGroupId")
				.securityGroupName("albSecurityGroup").allowAllOutbound(true).vpc(vpc).build();
		albSecurityGroup.addIngressRule(Peer.anyIpv4(), Port.tcp(8080)); 
		albSecurityGroup.addIngressRule(Peer.anyIpv4(), Port.tcp(8100));

		ISecurityGroup serviceSecurityGroup = SecurityGroup.Builder.create(this, "serviceSecurityGroupId")
				.securityGroupName("serviceSecurityGroup").allowAllOutbound(true).vpc(vpc).build();
		serviceSecurityGroup.addIngressRule(albSecurityGroup, Port.tcp(8080));

		// *******************************************//
		// ******ALB, Listeners, Target groups********//
		// **************Web APP**********************//

		ApplicationLoadBalancer alb = ApplicationLoadBalancer.Builder.create(this, "myALBId")
				.deletionProtection(false)
				.http2Enabled(true)
				.internetFacing(true)
				.loadBalancerName("myALB")
				.vpc(vpc)
				.vpcSubnets(SubnetSelection.builder().subnets(vpc.getPublicSubnets()).build())
				.securityGroup(albSecurityGroup).build();

		HealthCheck defaultHealthCheck = HealthCheck.builder().build();

		greenTargetGroup = ApplicationTargetGroup.Builder.create(this, "greenTGId")
				.targetGroupName("greenTG")
				.healthCheck(defaultHealthCheck)
				.port(8080)
				.protocol(ApplicationProtocol.HTTP)
				.protocolVersion(ApplicationProtocolVersion.HTTP1)
				.targetType(TargetType.IP)
				.vpc(vpc)
				.build();

		BaseApplicationListenerProps greenWebApplicationListenerProps = BaseApplicationListenerProps.builder()
				.port(8100)
				.protocol(ApplicationProtocol.HTTP)
				.open(true)
				.defaultAction(ListenerAction.forward(Arrays.asList(greenTargetGroup)))
				.build();
		greenWebListener = alb.addListener("greenListener", greenWebApplicationListenerProps);

		greenTargetGroup.registerListener(greenWebListener);

		// ********************************************************************//
		// *******ECS Cluster, task definition ECS Services with ALB***********//
		// ********************************************************************//

		Cluster cluster = Cluster.Builder.create(this, "MyClusterId").clusterName(ECS_CLUSTER_NAME).vpc(vpc)
				.build();


		// Create a load-balanced Fargate service and make it public
		ApplicationLoadBalancedFargateService fargateService = ApplicationLoadBalancedFargateService.Builder
				.create(this, "MyServiceId")
				.serviceName(ECS_SERVICE_NAME)
				.cluster(cluster)
				.loadBalancer(alb)
				.listenerPort(8080)
				.protocol(ApplicationProtocol.HTTP)
				.publicLoadBalancer(true)
				.taskImageOptions(
		                 ApplicationLoadBalancedTaskImageOptions.builder()
		                 	     .image(ContainerImage.fromRegistry("amazon/amazon-ecs-sample"))
		                         .build())
				.taskSubnets(SubnetSelection.builder()
						.subnets(vpc.getPrivateSubnets()).build())
						.assignPublicIp(false)
						.securityGroups(Arrays.asList(serviceSecurityGroup))
						.cpu(512)
						.desiredCount(2)
						.memoryLimitMiB(2048)
						.deploymentController(DeploymentController.builder().type(DeploymentControllerType.CODE_DEPLOY).build())
			    .build();

		blueTargetGroup = fargateService.getTargetGroup();
		blueWebListener = fargateService.getListener();

		// AutoScaling Policy
		EnableScalingProps enableWebScalingProps = EnableScalingProps.builder().minCapacity(2).maxCapacity(5).build();

		// CPU usage configuration
		CpuUtilizationScalingProps cpuWebUtilizationScalingProps = CpuUtilizationScalingProps.builder()
				.targetUtilizationPercent(40).scaleInCooldown(Duration.seconds(60))
				.scaleOutCooldown(Duration.seconds(60)).build();
		ScalableTaskCount scalableWebTaskCount = fargateService.getService().autoScaleTaskCount(enableWebScalingProps);
		scalableWebTaskCount.scaleOnCpuUtilization("CPUWebScalingPolicy", cpuWebUtilizationScalingProps);
		
		// ECS Task execution role
		RoleProps rolePropsExecRole = RoleProps.builder()
				.assumedBy(new ServicePrincipal("ecs-tasks.amazonaws.com"))
				.build();
		Role ecsTaskExecRole = new Role(this, "ecsTaskRoleSampleApp", rolePropsExecRole);
		ecsTaskExecRole.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName("service-role/AmazonECSTaskExecutionRolePolicy"));
		
		
		LogGroup logGroup = LogGroup.Builder.create(this, "lLogGroup")
				.logGroupName("awslogs-SampleApplication")
				.build();
		AwsLogDriverProps logDriverProps = AwsLogDriverProps.builder()
				.logGroup(logGroup)
				.streamPrefix("SampleApplication")
				.build();
		LogDriver.awsLogs(logDriverProps);
		
		
		// *******************************************//
	    // *******************ECR repo****************//
	    // *******************************************//
		
		software.amazon.awscdk.services.ecr.IRepository ecrRepo = software.amazon.awscdk.services.ecr.Repository.Builder.create(this, "EcrRepo")
				.repositoryName("sampleapp")
				.imageTagMutability(TagMutability.MUTABLE)
				.imageScanOnPush(false)
				.build();
		
		
		
		// *******************************************//
	    // *******************CDK outputs*************//
	    // *******************************************//
		
		CfnOutputProps ecrRepoOutputProps = CfnOutputProps.builder()
			.value(ecrRepo.getRepositoryUri())
			.description("ECR repository URI")
			.exportName("ECRRepository")
			.build();
		CfnOutput ecrRepoOutput = new CfnOutput(this, "ecrRepoOutput", ecrRepoOutputProps);
		
		
		CfnOutputProps ecsTaskExecRoleOutputProps = CfnOutputProps.builder()
				.value(ecsTaskExecRole.getRoleArn())
				.description("ECS task execution role ARN")
				.exportName("ECSTaskRoleExec")
				.build();
		CfnOutput ecsTaskExecRoleOutput = new CfnOutput(this, "ecsTaskRoleExecOutput", ecsTaskExecRoleOutputProps);

	}

}