from random import randint
from aws_cdk import core
from aws_cdk.aws_s3 import Bucket
from aws_cdk.aws_ec2 import Vpc
from aws_cdk.aws_elasticloadbalancingv2 import(
    ApplicationLoadBalancer,
    HealthCheck
)
from aws_cdk.aws_ecr_assets import DockerImageAsset

from aws_cdk.aws_ecs import(
    ContainerImage,
    Cluster,
    FargateTaskDefinition,
    LogDriver,
    PortMapping,
    FargateService,
    Protocol
)

from aws_cdk.aws_s3_deployment import(
    BucketDeployment,
    Source
)

from aws_cdk.aws_iam import(
    PolicyStatement,
    Effect
)

from aws_cdk.aws_dynamodb import(
    Table,
    AttributeType
)

from aws_cdk.aws_lambda import(
    SingletonFunction,
    InlineCode,
    Runtime
)

from aws_cdk.aws_cloudformation import(
    CustomResourceProvider,
    CustomResource
)



class FagateServerlessStack(core.Stack):

    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        # The code that defines your stack goes here

        vpc = Vpc(
            self, "MyVpc",
            max_azs=2
        )

        ecs_cluster = Cluster(
            self, 'FagateCluster',
            vpc=vpc
        )

        alb = ApplicationLoadBalancer(self, 'EcsLb', vpc=vpc, internet_facing=True)

        listener = alb.add_listener('EcsListener', port=80)

        listener.add_fixed_response('Default-Fix', status_code= '404')
        listener.node.default_child.default_action=[{
          "type": "fixed-response",
          "fixedResponseConfig": {"statusCode": "404"}
        }]
        
        website_bucket = Bucket(self, 'PetclinicWebsite',
            website_index_document='index.html',
            public_read_access=True,
            removal_policy=core.RemovalPolicy.DESTROY
        )
        
        deployment = BucketDeployment(self, 'PetclinicDeployWebsite',
          sources=[Source.asset('./spring-petclinic-static')],
          destination_bucket=website_bucket,
          retain_on_delete=False
          #destination_key_prefix='web/static'
        )
        
        # Modify the config.js with CF custome resource
        modify_policy = [PolicyStatement(
                actions=[ "s3:PutObject","s3:PutObjectAcl","s3:PutObjectVersionAcl","s3:GetObject"],
                effect=Effect.ALLOW,
                resources=[website_bucket.bucket_arn + "/*"]
            ),PolicyStatement(
                actions=[ "s3:ListBucket"],
                effect=Effect.ALLOW,
                resources=[website_bucket.bucket_arn]
            ),PolicyStatement(
                actions=[ "dynamodb:*"],
                effect=Effect.ALLOW,
                resources=["arn:aws:dynamodb:" + self.region + ":" + self.account + ":*"]
            )]
            
        with open("custom-resource-code/init.py", encoding="utf-8") as fp:
            code_body = fp.read()
        
        dynamodb_tables = []
        
        
        for s in ['customers', 'vets', 'visits']:
            table = Table(self, s.capitalize() + 'Table',
              partition_key={ 'name': 'id', 'type': AttributeType.STRING },
              removal_policy=core.RemovalPolicy.DESTROY,
              read_capacity=5,
              write_capacity=5,
            )
            
            dynamodb_tables.append(table.table_name)

            asset = DockerImageAsset(self, 'spring-petclinic-' + s,
              repository_name = self.stack_name + '-' + s,
              directory='./spring-petclinic-serverless/spring-petclinic-' + s + '-serverless',
              build_args={
                 'JAR_FILE': 'spring-petclinic-' + s + '-serverless-2.0.7.jar'
              })

            ecs_task = FargateTaskDefinition(
                self, 'TaskDef-Fargate-' + s,
                memory_limit_mib=512,
                cpu=256
            )

            ecs_task.add_to_task_role_policy(PolicyStatement(
                   actions=[ "dynamodb:*"],
                   effect=Effect.ALLOW,
                   resources=[table.table_arn]
            ))

            ecs_task.add_to_task_role_policy(PolicyStatement(
                   actions=['xray:*'],
                   effect=Effect.ALLOW,
                   resources=['*']
            ))

            env = {
              'DYNAMODB_TABLE_NAME': table.table_name,
              'SERVER_SERVLET_CONTEXT_PATH': '/api/' + s.rstrip('s')
            }

            ecs_container = ecs_task.add_container(
                'Container-' + s,
                image=ContainerImage.from_docker_image_asset(asset),
                logging=LogDriver.aws_logs(stream_prefix=s),
                environment=env
            )

            ecs_container.add_port_mappings(PortMapping(container_port=8080))

            # Sidecare Container for X-Ray
            ecs_sidecar_container = ecs_task.add_container(
                'Sidecar-Xray-' + s,
                image=ContainerImage.from_registry('amazon/aws-xray-daemon')
            )

            ecs_sidecar_container.add_port_mappings(PortMapping(container_port=2000, protocol=Protocol.UDP))

            ecs_service = FargateService(
                self, 'FargateService-' + s,
                cluster = ecs_cluster,
                service_name = 'spring-petclinic-' + s,
                desired_count = 2,
                task_definition = ecs_task
            )

            parttern = '/api/' + s.rstrip('s') + '/*'
            priority = randint(1, 10)*len(s)
            check=HealthCheck(
                path='/api/' + s.rstrip('s') + '/manage',
                healthy_threshold_count=2,
                unhealthy_threshold_count=3,
            )


            target = listener.add_targets(
                'ECS-' + s,
                path_pattern=parttern,
                priority = priority,
                port=80,
                targets=[ecs_service],
                health_check=check
            )
            
            
        resource = CustomResource(self, "S3ModifyCustomResource",
            provider=CustomResourceProvider.lambda_(
                SingletonFunction(
                    self, "CustomResourceSingleton",
                    uuid="f7d4f730-4ee1-11e8-9c2d-fa7ae01bbebc",
                    code=InlineCode(code_body),
                    handler="index.handler",
                    timeout=core.Duration.seconds(300),
                    runtime=Runtime.PYTHON_3_7,
                    initial_policy=modify_policy
                )
            ),
            properties={"Bucket": website_bucket.bucket_name, 
                        "InvokeUrl":'http://' + alb.load_balancer_dns_name +'/',
                        "DynamoDBTables": dynamodb_tables
            }
        )
        
        core.CfnOutput(self,"FagateALBUrl",export_name="FagateALBUrl",value=alb.load_balancer_dns_name)
        core.CfnOutput(self,"FagatePetclinicWebsiteUrl",export_name="FagatePetclinicWebsiteUrl",value=website_bucket.bucket_website_url)