#!/usr/bin/env bash
set -euo pipefail

TARGET_ROOT="${1:-.}"
TARGET_DIR="$TARGET_ROOT/deploy/aws-network-sensor"
BLACKSHIELD_SITE_URL="${BLACKSHIELD_SITE_URL:-https://app.blackshield.chaplau.com}"
BLACKSHIELD_API_URL="${BLACKSHIELD_API_URL:-https://api.blackshield.chaplau.com}"
BLACKSHIELD_NETWORK_SENSOR_IMAGE="${BLACKSHIELD_NETWORK_SENSOR_IMAGE:-public.ecr.aws/blackshield-security/network-sensor:1.0.0}"

replace_literal() {
  local file_path="$1"
  local placeholder="$2"
  local replacement="$3"
  local tmp_file="${file_path}.tmp.$$"

  sed "s|${placeholder}|${replacement}|g" "$file_path" > "$tmp_file"
  mv "$tmp_file" "$file_path"
}

mkdir -p "$TARGET_DIR"

cat > "$TARGET_DIR/app.py" <<EOF
#!/usr/bin/env python3
"""CDK app entry point for the BlackShield AWS network sensor."""

import os

import aws_cdk as cdk

from network_sensor_stack import NetworkSensorStack

app = cdk.App()

env = cdk.Environment(
    account=os.environ.get("CDK_DEFAULT_ACCOUNT"),
    region=os.environ.get("CDK_DEFAULT_REGION", "us-east-1"),
)

NetworkSensorStack(
    app,
    app.node.try_get_context("stack_name") or "BlackShieldAwsNetworkSensor",
    env=env,
    vpc_id=app.node.try_get_context("vpc_id"),
    subnet_id=app.node.try_get_context("subnet_id"),
    blackshield_api_url=(
        app.node.try_get_context("api_url") or os.environ.get("BLACKSHIELD_API_URL", "${BLACKSHIELD_API_URL}")
    ),
    sensor_secret_name=(
        app.node.try_get_context("secret_name")
        or os.environ.get("SECRET_NAME", "blackshield/network-sensor-key-prod")
    ),
    sensor_type=app.node.try_get_context("sensor_type") or "suricata",
    min_severity=app.node.try_get_context("min_severity") or "high",
    key_name=app.node.try_get_context("key_name"),
)

cdk.Tags.of(app).add("Project", "SecurityPlatform")
cdk.Tags.of(app).add("ManagedBy", "CDK")
cdk.Tags.of(app).add("Component", "network-sensor")

app.synth()
EOF

cat > "$TARGET_DIR/cdk.json" <<EOF
{
  "app": "python app.py",
  "context": {
    "stack_name": "BlackShieldAwsNetworkSensor",
    "vpc_id": "vpc-REPLACE_ME",
    "subnet_id": "subnet-REPLACE_ME",
    "api_url": "${BLACKSHIELD_API_URL}",
    "secret_name": "blackshield/network-sensor-key-prod",
    "sensor_type": "suricata",
    "min_severity": "high",
    "key_name": null
  }
}
EOF

cat > "$TARGET_DIR/network_sensor_stack.py" <<'EOF'
"""CDK stack for the BlackShield AWS network sensor."""

from __future__ import annotations

from aws_cdk import CfnOutput, Duration, Stack, Tags
from aws_cdk import aws_ec2 as ec2
from aws_cdk import aws_iam as iam
from aws_cdk import aws_logs as logs
from constructs import Construct


class NetworkSensorStack(Stack):
    """Deploy an EC2-based network sensor with Traffic Mirroring support."""

    def __init__(
        self,
        scope: Construct,
        construct_id: str,
        *,
        vpc_id: str | None,
        subnet_id: str | None,
        blackshield_api_url: str,
        sensor_secret_name: str,
        sensor_type: str,
        min_severity: str,
        key_name: str | None = None,
        **kwargs: object,
    ) -> None:
        super().__init__(scope, construct_id, **kwargs)

        if not vpc_id or not subnet_id:
            raise ValueError(
                "Missing required CDK context. Set vpc_id and subnet_id in cdk.json or pass them with -c."
            )

        vpc = ec2.Vpc.from_lookup(self, "Vpc", vpc_id=vpc_id)
        subnet = ec2.Subnet.from_subnet_id(self, "Subnet", subnet_id=subnet_id)

        log_group = logs.LogGroup(
            self,
            "NetworkSensorLogs",
            retention=logs.RetentionDays.TWO_WEEKS,
        )

        role = iam.Role(
            self,
            "NetworkSensorRole",
            assumed_by=iam.ServicePrincipal("ec2.amazonaws.com"),
            managed_policies=[
                iam.ManagedPolicy.from_aws_managed_policy_name("AmazonSSMManagedInstanceCore"),
            ],
        )
        role.add_to_policy(
            iam.PolicyStatement(
                actions=["secretsmanager:GetSecretValue"],
                resources=[
                    f"arn:aws:secretsmanager:{self.region}:{self.account}:secret:{sensor_secret_name}*"
                ],
            )
        )
        role.add_to_policy(
            iam.PolicyStatement(
                actions=["logs:CreateLogStream", "logs:PutLogEvents"],
                resources=[f"{log_group.log_group_arn}:*"],
            )
        )

        security_group = ec2.SecurityGroup(
            self,
            "NetworkSensorSecurityGroup",
            vpc=vpc,
            description="Allow VXLAN ingress and HTTPS egress for the BlackShield network sensor",
        )
        security_group.add_ingress_rule(ec2.Peer.ipv4("10.0.0.0/8"), ec2.Port.udp(4789), "VXLAN")
        security_group.add_egress_rule(ec2.Peer.any_ipv4(), ec2.Port.tcp(443), "HTTPS egress")

        machine_image = ec2.MachineImage.latest_amazon_linux2023()
        instance = ec2.Instance(
            self,
            "NetworkSensorInstance",
            vpc=vpc,
            vpc_subnets=ec2.SubnetSelection(subnets=[subnet]),
            instance_type=ec2.InstanceType("t3.medium"),
            machine_image=machine_image,
            security_group=security_group,
            role=role,
            key_name=key_name,
        )

        instance.add_user_data(
            "#!/bin/bash",
            "set -euo pipefail",
            "dnf update -y",
            "dnf install -y docker awscli",
            "systemctl enable --now docker",
            f"API_KEY=$(aws secretsmanager get-secret-value --secret-id '{sensor_secret_name}' --region '{self.region}' --query SecretString --output text)",
            "docker pull public.ecr.aws/blackshield-security/network-sensor:latest",
            "docker rm -f network-sensor || true",
            "docker run -d \\",
            "  --name network-sensor \\",
            "  --network host \\",
            "  --cap-add NET_ADMIN \\",
            "  --cap-add NET_RAW \\",
            "  --restart unless-stopped \\",
            f"  -e BLACKSHIELD_API_URL='{blackshield_api_url}' \\",
            "  -e BLACKSHIELD_API_KEY=\"$API_KEY\" \\",
            f"  -e SENSOR_TYPE='{sensor_type}' \\",
            "  -e SENSOR_INTERFACE='eth0' \\",
            f"  -e MIN_SEVERITY='{min_severity}' \\",
            "  -e SCAN_INTERVAL_SECONDS='300' \\",
            "  public.ecr.aws/blackshield-security/network-sensor:latest",
        )

        Tags.of(instance).add("Component", "NetworkSensor")

        CfnOutput(self, "InstanceId", value=instance.instance_id)
        CfnOutput(self, "PrivateIp", value=instance.instance_private_ip)
        CfnOutput(self, "SecurityGroupId", value=security_group.security_group_id)
        CfnOutput(self, "LogGroupName", value=log_group.log_group_name)
EOF

replace_literal \
  "$TARGET_DIR/network_sensor_stack.py" \
  "public.ecr.aws/blackshield-security/network-sensor:latest" \
  "$BLACKSHIELD_NETWORK_SENSOR_IMAGE"

cat > "$TARGET_DIR/requirements.txt" <<'EOF'
aws-cdk-lib>=2.130.0
constructs>=10.0.0
EOF

cat > "$TARGET_DIR/README.md" <<'EOF'
# AWS Network Sensor CDK Project

This source bundle creates a deployable AWS CDK project for the BlackShield network sensor.

## Usage

```bash
bash <(curl -fsSL https://app.blackshield.chaplau.com/source-bundles/aws-network-sensor.sh)
cd deploy/aws-network-sensor
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cdk bootstrap
cdk deploy --require-approval never
```

## Required edits

Update cdk.json before deploy:
- vpc_id
- subnet_id
- secret_name
- sensor_type
- min_severity

`api_url` is already prefilled from the guide runtime. Override it only if you need to point the sensor at a different BlackShield environment.

## Secret format

Store your ingestion API key in AWS Secrets Manager as a plain string.
Example secret name: blackshield/network-sensor-key-prod
EOF

replace_literal "$TARGET_DIR/README.md" "https://app.blackshield.chaplau.com" "$BLACKSHIELD_SITE_URL"

printf 'Wrote source bundle files:\n'
printf '  - %s\n' "$TARGET_DIR/app.py"
printf '  - %s\n' "$TARGET_DIR/cdk.json"
printf '  - %s\n' "$TARGET_DIR/network_sensor_stack.py"
printf '  - %s\n' "$TARGET_DIR/requirements.txt"
printf '  - %s\n' "$TARGET_DIR/README.md"
