AWSTemplateFormatVersion: '2010-09-09'
Description: >
  ThreatWerk ECS Fargate Stack - provisions VPC, ALB, ECS Fargate services,
  RDS PostgreSQL, ECR repositories, Secrets Manager, and IAM roles.
  This is the recommended quick-start deployment path.

# ==============================================================================
# Parameters
# ==============================================================================
Parameters:
  Environment:
    Type: String
    Default: production
    AllowedValues: [production, staging]
    Description: Environment name (used in resource naming and tagging)

  DomainName:
    Type: String
    Description: >
      Domain name for the ThreatWerk application (e.g. threatwerk.yourcompany.com).
      An ACM certificate will be created for this domain - you must validate it via DNS.

  VpcCidr:
    Type: String
    Default: '10.0.0.0/16'
    Description: CIDR block for the VPC

  AllowedInboundCidr:
    Type: String
    Default: '0.0.0.0/0'
    Description: >
      CIDR range allowed to reach the ALB on port 443. STRONGLY RECOMMENDED to
      restrict this to your corporate network or VPN CIDR (e.g. 203.0.113.0/24).
      Leaving this as 0.0.0.0/0 exposes the application to the entire internet.

  ALBScheme:
    Type: String
    Default: internet-facing
    AllowedValues: [internet-facing, internal]
    Description: >
      ALB scheme. Use "internal" if users connect via VPN/Direct Connect.
      Use "internet-facing" only with a restricted AllowedInboundCidr.

  RDSInstanceClass:
    Type: String
    Default: db.t3.medium
    AllowedValues: [db.t3.micro, db.t3.small, db.t3.medium, db.t3.large, db.r6g.large]
    Description: >
      RDS instance class. Use db.t3.micro for test installs (not Multi-AZ capable
      on free tier). Use db.t3.medium+ for production.

  RDSMultiAZ:
    Type: String
    Default: 'true'
    AllowedValues: ['true', 'false']
    Description: >
      Enable Multi-AZ for the RDS instance. Recommended for production.
      Set to false for test/staging to save cost.

  DBPassword:
    Type: String
    NoEcho: true
    MinLength: 16
    Description: >
      Master password for the RDS instance (min 16 characters). This is the
      RDS master user password - you will create a separate application user
      after the stack is deployed.

  BackendImageTag:
    Type: String
    Default: latest
    Description: Docker image tag for the backend container

  FrontendImageTag:
    Type: String
    Default: latest
    Description: Docker image tag for the frontend container

  BackendCpu:
    Type: Number
    Default: 512
    AllowedValues: [256, 512, 1024, 2048]
    Description: CPU units for the backend task (256 = 0.25 vCPU, 512 = 0.5 vCPU)

  BackendMemory:
    Type: Number
    Default: 1024
    AllowedValues: [512, 1024, 2048, 4096]
    Description: Memory (MB) for the backend task

  BackendDesiredCount:
    Type: Number
    Default: 2
    MinValue: 1
    MaxValue: 10
    Description: Number of backend task replicas

  FrontendCpu:
    Type: Number
    Default: 256
    AllowedValues: [256, 512]
    Description: CPU units for the frontend task

  FrontendMemory:
    Type: Number
    Default: 512
    AllowedValues: [512, 1024]
    Description: Memory (MB) for the frontend task

# ==============================================================================
# Conditions
# ==============================================================================
Conditions:
  IsMultiAZ: !Equals [!Ref RDSMultiAZ, 'true']

# ==============================================================================
# Resources
# ==============================================================================
Resources:

  # ============================================================================
  # VPC and Networking
  # ============================================================================
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VpcCidr
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-vpc'

  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-igw'

  IGWAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway

  # Public Subnets (ALB + NAT Gateway)
  PublicSubnetA:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: !Select [0, !Cidr [!Ref VpcCidr, 8, 8]]
      AvailabilityZone: !Select [0, !GetAZs '']
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-public-a'

  PublicSubnetB:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: !Select [1, !Cidr [!Ref VpcCidr, 8, 8]]
      AvailabilityZone: !Select [1, !GetAZs '']
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-public-b'

  # Private Subnets (ECS tasks)
  PrivateSubnetA:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: !Select [2, !Cidr [!Ref VpcCidr, 8, 8]]
      AvailabilityZone: !Select [0, !GetAZs '']
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-private-a'

  PrivateSubnetB:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: !Select [3, !Cidr [!Ref VpcCidr, 8, 8]]
      AvailabilityZone: !Select [1, !GetAZs '']
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-private-b'

  # Database Subnets (isolated - no internet access)
  DBSubnetA:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: !Select [4, !Cidr [!Ref VpcCidr, 8, 8]]
      AvailabilityZone: !Select [0, !GetAZs '']
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-db-a'

  DBSubnetB:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: !Select [5, !Cidr [!Ref VpcCidr, 8, 8]]
      AvailabilityZone: !Select [1, !GetAZs '']
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-db-b'

  # NAT Gateway (outbound internet for private subnets)
  NATElasticIP:
    Type: AWS::EC2::EIP
    DependsOn: IGWAttachment
    Properties:
      Domain: vpc
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-nat-eip'

  NATGateway:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt NATElasticIP.AllocationId
      SubnetId: !Ref PublicSubnetA
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-nat'

  # Route Tables
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-public-rt'

  PublicRoute:
    Type: AWS::EC2::Route
    DependsOn: IGWAttachment
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: '0.0.0.0/0'
      GatewayId: !Ref InternetGateway

  PublicSubnetARouteAssoc:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnetA
      RouteTableId: !Ref PublicRouteTable

  PublicSubnetBRouteAssoc:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnetB
      RouteTableId: !Ref PublicRouteTable

  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-private-rt'

  PrivateRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      DestinationCidrBlock: '0.0.0.0/0'
      NatGatewayId: !Ref NATGateway

  PrivateSubnetARouteAssoc:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnetA
      RouteTableId: !Ref PrivateRouteTable

  PrivateSubnetBRouteAssoc:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnetB
      RouteTableId: !Ref PrivateRouteTable

  DBRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-db-rt'

  DBSubnetARouteAssoc:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref DBSubnetA
      RouteTableId: !Ref DBRouteTable

  DBSubnetBRouteAssoc:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref DBSubnetB
      RouteTableId: !Ref DBRouteTable

  # ============================================================================
  # Security Groups
  # ============================================================================
  ALBSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: !Sub 'ThreatWerk ${Environment} ALB - inbound HTTPS'
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: !Ref AllowedInboundCidr
          Description: !Sub 'HTTPS from ${AllowedInboundCidr}'
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-alb-sg'

  ECSSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: !Sub 'ThreatWerk ${Environment} ECS tasks'
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 8080
          ToPort: 8080
          SourceSecurityGroupId: !Ref ALBSecurityGroup
          Description: Traffic from ALB
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-ecs-sg'

  # ECS task egress - AWS APIs, intel feeds, and database
  ECSEgressHTTPS:
    Type: AWS::EC2::SecurityGroupEgress
    Properties:
      GroupId: !Ref ECSSecurityGroup
      IpProtocol: tcp
      FromPort: 443
      ToPort: 443
      CidrIp: '0.0.0.0/0'
      Description: AWS APIs, ECR, and intel feeds

  ECSEgressDB:
    Type: AWS::EC2::SecurityGroupEgress
    Properties:
      GroupId: !Ref ECSSecurityGroup
      IpProtocol: tcp
      FromPort: 5432
      ToPort: 5432
      DestinationSecurityGroupId: !Ref RDSSecurityGroup
      Description: PostgreSQL connections

  RDSSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: !Sub 'ThreatWerk ${Environment} RDS - inbound from ECS only'
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 5432
          ToPort: 5432
          SourceSecurityGroupId: !Ref ECSSecurityGroup
          Description: PostgreSQL from ECS tasks
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-rds-sg'

  # ALB egress to ECS tasks
  ALBEgressToECS:
    Type: AWS::EC2::SecurityGroupEgress
    Properties:
      GroupId: !Ref ALBSecurityGroup
      IpProtocol: tcp
      FromPort: 8080
      ToPort: 8080
      DestinationSecurityGroupId: !Ref ECSSecurityGroup
      Description: Forward traffic to ECS tasks

  # ============================================================================
  # ECR Repositories
  # ============================================================================
  BackendRepository:
    Type: AWS::ECR::Repository
    Properties:
      RepositoryName: !Sub 'threatwerk/${Environment}/backend'
      ImageScanningConfiguration:
        ScanOnPush: true
      EncryptionConfiguration:
        EncryptionType: AES256
      LifecyclePolicy:
        LifecyclePolicyText: |
          {
            "rules": [{
              "rulePriority": 1,
              "description": "Keep last 10 images",
              "selection": {
                "tagStatus": "any",
                "countType": "imageCountMoreThan",
                "countNumber": 10
              },
              "action": { "type": "expire" }
            }]
          }
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-backend'

  FrontendRepository:
    Type: AWS::ECR::Repository
    Properties:
      RepositoryName: !Sub 'threatwerk/${Environment}/frontend'
      ImageScanningConfiguration:
        ScanOnPush: true
      EncryptionConfiguration:
        EncryptionType: AES256
      LifecyclePolicy:
        LifecyclePolicyText: |
          {
            "rules": [{
              "rulePriority": 1,
              "description": "Keep last 10 images",
              "selection": {
                "tagStatus": "any",
                "countType": "imageCountMoreThan",
                "countNumber": 10
              },
              "action": { "type": "expire" }
            }]
          }
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-frontend'

  # ============================================================================
  # ACM Certificate
  # ============================================================================
  Certificate:
    Type: AWS::CertificateManager::Certificate
    Properties:
      DomainName: !Ref DomainName
      ValidationMethod: DNS
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-cert'

  # ============================================================================
  # RDS PostgreSQL
  # ============================================================================
  DBSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties:
      DBSubnetGroupDescription: !Sub 'ThreatWerk ${Environment} RDS subnet group'
      SubnetIds:
        - !Ref DBSubnetA
        - !Ref DBSubnetB
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-db-subnets'

  RDSInstance:
    Type: AWS::RDS::DBInstance
    DeletionPolicy: Snapshot
    UpdateReplacePolicy: Snapshot
    Properties:
      DBInstanceIdentifier: !Sub 'threatwerk-${Environment}-db'
      DBInstanceClass: !Ref RDSInstanceClass
      Engine: postgres
      EngineVersion: '16.4'
      MasterUsername: masteruser
      MasterUserPassword: !Ref DBPassword
      DBName: threatwerk
      AllocatedStorage: 50
      StorageType: gp3
      StorageEncrypted: true
      MultiAZ: !If [IsMultiAZ, true, false]
      DBSubnetGroupName: !Ref DBSubnetGroup
      VPCSecurityGroups:
        - !Ref RDSSecurityGroup
      PubliclyAccessible: false
      BackupRetentionPeriod: 7
      DeletionProtection: true
      CopyTagsToSnapshot: true
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-db'

  # ============================================================================
  # Secrets Manager
  # ============================================================================
  AppSecret:
    Type: AWS::SecretsManager::Secret
    Properties:
      Name: !Sub 'threatwerk-${Environment}-secrets'
      Description: >
        ThreatWerk application secrets. CRITICAL: You MUST replace JWT_SECRET
        and ENCRYPTION_KEY with strong random values BEFORE the first deployment.
        The application will refuse to start with the placeholder values.
        Generate with: openssl rand -base64 48
      SecretString: !Sub |
        {
          "DATABASE_URL": "postgres://masteruser:${DBPassword}@${RDSInstance.Endpoint.Address}:5432/threatwerk?sslmode=require",
          "JWT_SECRET": "CHANGE_ME",
          "ENCRYPTION_KEY": "CHANGE_ME"
        }
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-secrets'

  # ============================================================================
  # ECS Cluster
  # ============================================================================
  ECSCluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: !Sub 'threatwerk-${Environment}'
      ClusterSettings:
        - Name: containerInsights
          Value: enabled
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}'

  # ============================================================================
  # CloudWatch Log Groups
  # ============================================================================
  BackendLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub '/ecs/threatwerk-${Environment}/backend'
      RetentionInDays: 30

  FrontendLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub '/ecs/threatwerk-${Environment}/frontend'
      RetentionInDays: 14

  # ============================================================================
  # IAM Roles
  # ============================================================================

  # Task Execution Role - used by ECS agent to pull images and write logs
  TaskExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub 'threatwerk-${Environment}-task-execution'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ecs-tasks.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
      Policies:
        - PolicyName: ECRPull
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - ecr:GetAuthorizationToken
                Resource: '*'
              - Effect: Allow
                Action:
                  - ecr:BatchCheckLayerAvailability
                  - ecr:GetDownloadUrlForLayer
                  - ecr:BatchGetImage
                Resource:
                  - !GetAtt BackendRepository.Arn
                  - !GetAtt FrontendRepository.Arn

  # Task Role - used by the application container at runtime
  BackendTaskRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub 'threatwerk-${Environment}-backend-task'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ecs-tasks.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: ThreatWerkBackendPolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Sid: SecretsManagerRead
                Effect: Allow
                Action:
                  - secretsmanager:GetSecretValue
                Resource:
                  - !Ref AppSecret
              - Sid: LicenseManager
                Effect: Allow
                Action:
                  - license-manager:CheckoutLicense
                  - license-manager:CheckInLicense
                  - license-manager:ExtendLicenseConsumption
                  - license-manager:GetLicense
                  - license-manager:ListReceivedLicenses
                Resource: '*'
              - Sid: ECSExec
                Effect: Allow
                Action:
                  - ssmmessages:CreateControlChannel
                  - ssmmessages:CreateDataChannel
                  - ssmmessages:OpenControlChannel
                  - ssmmessages:OpenDataChannel
                Resource: '*'
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-backend-task'

  # ============================================================================
  # ALB
  # ============================================================================
  ALB:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: !Sub 'tw-${Environment}'
      Scheme: !Ref ALBScheme
      Type: application
      SecurityGroups:
        - !Ref ALBSecurityGroup
      Subnets:
        - !Ref PublicSubnetA
        - !Ref PublicSubnetB
      LoadBalancerAttributes:
        - Key: idle_timeout.timeout_seconds
          Value: '180'
        - Key: routing.http.drop_invalid_header_fields.enabled
          Value: 'true'
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-alb'

  ALBListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref ALB
      Port: 443
      Protocol: HTTPS
      Certificates:
        - CertificateArn: !Ref Certificate
      SslPolicy: ELBSecurityPolicy-TLS13-1-2-2021-06
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref FrontendTargetGroup

  # Backend target group - /api/* and /ws/* routes
  BackendTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Sub 'tw-${Environment}-backend'
      Port: 8080
      Protocol: HTTP
      VpcId: !Ref VPC
      TargetType: ip
      HealthCheckPath: /health
      HealthCheckIntervalSeconds: 15
      HealthyThresholdCount: 2
      UnhealthyThresholdCount: 3

  # Frontend target group - default (all other routes)
  FrontendTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Sub 'tw-${Environment}-frontend'
      Port: 8080
      Protocol: HTTP
      VpcId: !Ref VPC
      TargetType: ip
      HealthCheckPath: /
      HealthCheckIntervalSeconds: 30
      HealthyThresholdCount: 2
      UnhealthyThresholdCount: 3

  # Listener rules - route /api/ and /ws/ to backend
  APIListenerRule:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Properties:
      ListenerArn: !Ref ALBListener
      Priority: 10
      Conditions:
        - Field: path-pattern
          Values: ['/api/*']
      Actions:
        - Type: forward
          TargetGroupArn: !Ref BackendTargetGroup

  WSListenerRule:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Properties:
      ListenerArn: !Ref ALBListener
      Priority: 20
      Conditions:
        - Field: path-pattern
          Values: ['/ws/*']
      Actions:
        - Type: forward
          TargetGroupArn: !Ref BackendTargetGroup

  MCPListenerRule:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Properties:
      ListenerArn: !Ref ALBListener
      Priority: 30
      Conditions:
        - Field: path-pattern
          Values: ['/mcp*']
      Actions:
        - Type: forward
          TargetGroupArn: !Ref BackendTargetGroup

  # ============================================================================
  # ECS Task Definitions
  # ============================================================================
  BackendTaskDef:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: !Sub 'threatwerk-${Environment}-backend'
      Cpu: !Ref BackendCpu
      Memory: !Ref BackendMemory
      NetworkMode: awsvpc
      RequiresCompatibilities: [FARGATE]
      ExecutionRoleArn: !GetAtt TaskExecutionRole.Arn
      TaskRoleArn: !GetAtt BackendTaskRole.Arn
      ContainerDefinitions:
        - Name: backend
          Image: !Sub '${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${BackendRepository}:${BackendImageTag}'
          Essential: true
          PortMappings:
            - ContainerPort: 8080
              Protocol: tcp
          Environment:
            - Name: LISTEN_ADDR
              Value: ':8080'
            - Name: MIGRATIONS_DIR
              Value: /migrations
            - Name: SECRETS_MANAGER_ARN
              Value: !Ref AppSecret
            - Name: APP_URL
              Value: !Sub 'https://${DomainName}'
            - Name: LICENSE_MODE
              Value: aws_marketplace
            - Name: AWS_REGION
              Value: !Ref AWS::Region
          LogConfiguration:
            LogDriver: awslogs
            Options:
              awslogs-group: !Ref BackendLogGroup
              awslogs-region: !Ref AWS::Region
              awslogs-stream-prefix: backend
          HealthCheck:
            Command: ['CMD-SHELL', 'wget -qO- http://localhost:8080/health || exit 1']
            Interval: 15
            Timeout: 5
            Retries: 3
            StartPeriod: 30

  FrontendTaskDef:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: !Sub 'threatwerk-${Environment}-frontend'
      Cpu: !Ref FrontendCpu
      Memory: !Ref FrontendMemory
      NetworkMode: awsvpc
      RequiresCompatibilities: [FARGATE]
      ExecutionRoleArn: !GetAtt TaskExecutionRole.Arn
      ContainerDefinitions:
        - Name: frontend
          Image: !Sub '${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${FrontendRepository}:${FrontendImageTag}'
          Essential: true
          PortMappings:
            - ContainerPort: 8080
              Protocol: tcp
          LogConfiguration:
            LogDriver: awslogs
            Options:
              awslogs-group: !Ref FrontendLogGroup
              awslogs-region: !Ref AWS::Region
              awslogs-stream-prefix: frontend
          HealthCheck:
            Command: ['CMD-SHELL', 'wget -qO- http://localhost:8080/ || exit 1']
            Interval: 30
            Timeout: 5
            Retries: 3
            StartPeriod: 10

  # ============================================================================
  # ECS Services
  # ============================================================================
  BackendService:
    Type: AWS::ECS::Service
    DependsOn:
      - ALBListener
      - APIListenerRule
    Properties:
      ServiceName: !Sub 'threatwerk-${Environment}-backend'
      Cluster: !Ref ECSCluster
      TaskDefinition: !Ref BackendTaskDef
      LaunchType: FARGATE
      DesiredCount: !Ref BackendDesiredCount
      EnableExecuteCommand: true
      NetworkConfiguration:
        AwsvpcConfiguration:
          AssignPublicIp: DISABLED
          SecurityGroups:
            - !Ref ECSSecurityGroup
          Subnets:
            - !Ref PrivateSubnetA
            - !Ref PrivateSubnetB
      LoadBalancers:
        - ContainerName: backend
          ContainerPort: 8080
          TargetGroupArn: !Ref BackendTargetGroup
      DeploymentConfiguration:
        MinimumHealthyPercent: 100
        MaximumPercent: 200

  FrontendService:
    Type: AWS::ECS::Service
    DependsOn:
      - ALBListener
    Properties:
      ServiceName: !Sub 'threatwerk-${Environment}-frontend'
      Cluster: !Ref ECSCluster
      TaskDefinition: !Ref FrontendTaskDef
      LaunchType: FARGATE
      DesiredCount: 1
      NetworkConfiguration:
        AwsvpcConfiguration:
          AssignPublicIp: DISABLED
          SecurityGroups:
            - !Ref ECSSecurityGroup
          Subnets:
            - !Ref PrivateSubnetA
            - !Ref PrivateSubnetB
      LoadBalancers:
        - ContainerName: frontend
          ContainerPort: 8080
          TargetGroupArn: !Ref FrontendTargetGroup
      DeploymentConfiguration:
        MinimumHealthyPercent: 100
        MaximumPercent: 200

# ==============================================================================
# Outputs
# ==============================================================================
Outputs:
  VpcId:
    Description: VPC ID
    Value: !Ref VPC
    Export:
      Name: !Sub '${AWS::StackName}-VpcId'

  ALBDNSName:
    Description: >
      ALB DNS name. Point your domain CNAME (or Route 53 alias) to this value.
    Value: !GetAtt ALB.DNSName

  ALBHostedZoneId:
    Description: ALB hosted zone ID (for Route 53 alias records)
    Value: !GetAtt ALB.CanonicalHostedZoneID

  RDSEndpoint:
    Description: RDS instance endpoint
    Value: !GetAtt RDSInstance.Endpoint.Address
    Export:
      Name: !Sub '${AWS::StackName}-RDSEndpoint'

  SecretsManagerARN:
    Description: >
      Secrets Manager secret ARN. Update this secret with DATABASE_URL,
      JWT_SECRET, and ENCRYPTION_KEY after stack creation.
    Value: !Ref AppSecret
    Export:
      Name: !Sub '${AWS::StackName}-SecretsARN'

  BackendECRUri:
    Description: ECR repository URI for the backend image
    Value: !Sub '${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${BackendRepository}'

  FrontendECRUri:
    Description: ECR repository URI for the frontend image
    Value: !Sub '${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${FrontendRepository}'

  CertificateARN:
    Description: >
      ACM certificate ARN. You must validate this certificate by adding the
      DNS CNAME record shown in the ACM console before the stack completes.
    Value: !Ref Certificate

  ECSClusterName:
    Description: ECS cluster name (for deploying updates)
    Value: !Ref ECSCluster

  BackendServiceName:
    Description: Backend ECS service name (for force-new-deployment)
    Value: !GetAtt BackendService.Name

  FrontendServiceName:
    Description: Frontend ECS service name
    Value: !GetAtt FrontendService.Name

  BackendTaskRoleARN:
    Description: >
      Backend task role ARN. Add SES/S3 permissions here if needed.
    Value: !GetAtt BackendTaskRole.Arn

  PostDeploymentInstructions:
    Description: Steps to complete after stack creation
    Value: >
      1. Validate the ACM certificate (add the DNS CNAME shown in ACM console).
      2. Push container images to the ECR repositories shown in outputs.
      3. Update Secrets Manager: replace JWT_SECRET and ENCRYPTION_KEY with strong
         random values (openssl rand -base64 48). DATABASE_URL is pre-configured.
      4. Point your domain DNS to the ALB DNS name.
      5. Force a new deployment to pick up the updated secrets:
         aws ecs update-service --cluster <cluster> --service <backend-service> --force-new-deployment
