# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0

from aws_cdk import (
    core,
    aws_dynamodb,
    aws_lambda,
    aws_iam,
    aws_events,
    aws_events_targets,
)


class HubStack(core.Stack):
    """
    This class deploys the resources needed to operate the Hub of this solution.
    The list of resources created is:

    * A DDB table, a lambda to write new items into it, and an EventBridge rule to trigger the lambda
    * a lambda to setup the connection to al new spoke
    * a lambda to request new data from all the spokes
    """

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

        table = aws_dynamodb.Table(
            self,
            id="ds-dashboard-hub-table",
            table_name="ds-dashboard-hub-table",
            partition_key=aws_dynamodb.Attribute(
                name="MetricName", type=aws_dynamodb.AttributeType.STRING
            ),
            sort_key=aws_dynamodb.Attribute(
                name="ExtractionDate", type=aws_dynamodb.AttributeType.STRING
            ),
            point_in_time_recovery=True,
            removal_policy=core.RemovalPolicy.DESTROY,
        )

        # This lambda is triggered by events arriving from the spoke accounts and writes to ddb
        dynamo_write_lambda = aws_lambda.Function(
            self,
            "ds-dashboard-dynamo-write",
            function_name="ds-dashboard-dynamo-write",
            runtime=aws_lambda.Runtime.PYTHON_3_9,
            code=aws_lambda.Code.asset("lambda_function_code"),
            handler="dynamo_write.lambda_handler",
            timeout=core.Duration.minutes(1),
            memory_size=128,
            environment={"DDB_TABLE_NAME": table.table_name},
        )

        dynamo_rule = aws_events.Rule(
            self,
            id="eb_to_ddb_rule",
            rule_name="eb_to_ddb_rule",
            description="eb_to_ddb_rule",
            enabled=True,
            event_pattern=aws_events.EventPattern(
                source=["metric_extractor"],
                detail_type=["metric_extractor"],
            ),
        )

        dynamo_rule.apply_removal_policy(core.RemovalPolicy.DESTROY)

        dynamo_rule.add_target(aws_events_targets.LambdaFunction(dynamo_write_lambda))

        dynamo_write_lambda.add_permission(
            "fromEB",
            principal=aws_iam.ServicePrincipal("events.amazonaws.com"),
            action="lambda:InvokeFunction",
            source_arn=dynamo_rule.rule_arn,
        )

        table.grant_write_data(dynamo_write_lambda)

        # this lambda configures the connection to the spokes.
        dashboard_connection_lambda = aws_lambda.Function(
            self,
            "ds-dashboard-connection",
            function_name="ds-dashboard-connection",
            runtime=aws_lambda.Runtime.PYTHON_3_9,
            code=aws_lambda.Code.asset("lambda_function_code"),
            handler="dashboard_connection.lambda_handler",
            timeout=core.Duration.minutes(1),
            memory_size=128,
        )

        eb_policy_statement = aws_iam.PolicyStatement(
            actions=[
                "events:PutRule",
                "events:PutTargets",
                "events:PutPermission",
                "ssm:GetParametersByPath",
            ],
            resources=["*"],
        )

        dashboard_connection_lambda.role.add_to_policy(eb_policy_statement)

        # a third lambda, just a utility function to emit a custom event. the event will be forwarded to all spokes and will trigger there new extractions

        fetch_new_data = aws_lambda.Function(
            self,
            "ds-dashboard-fetch-new-data",
            function_name="ds-dashboard-fetch-new-data",
            runtime=aws_lambda.Runtime.PYTHON_3_9,
            code=aws_lambda.Code.asset("lambda_function_code"),
            handler="fetch_metric_values.lambda_handler",
            timeout=core.Duration.minutes(1),
            memory_size=128,
        )

        fetch_policy_statement = aws_iam.PolicyStatement(
            actions=["events:PutEvents"], resources=["*"]
        )

        fetch_new_data.role.add_to_policy(fetch_policy_statement)