AWSTemplateFormatVersion: '2010-09-09'
Description: >
  ThreatWerk Infrastructure Stack - provisions VPC, EKS, RDS, Secrets Manager,
  and supporting resources for a production ThreatWerk deployment.

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

  DomainName:
    Type: String
    Description: Domain name for the ThreatWerk application (e.g. threatwerk.yourcompany.com)

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

  EKSNodeInstanceType:
    Type: String
    Default: t3.medium
    AllowedValues: [t3.medium, t3.large, m6i.large, m6i.xlarge]
    Description: EC2 instance type for EKS worker nodes

  EKSNodeCount:
    Type: Number
    Default: 2
    MinValue: 2
    MaxValue: 10
    Description: Number of EKS worker nodes

  RDSInstanceClass:
    Type: String
    Default: db.t3.medium
    AllowedValues: [db.t3.medium, db.t3.large, db.r6g.large, db.r6g.xlarge]
    Description: RDS instance class

  DBPassword:
    Type: String
    NoEcho: true
    MinLength: 16
    Description: Master password for the RDS instance (min 16 characters)

  EKSVersion:
    Type: String
    Default: '1.29'
    AllowedValues: ['1.27', '1.28', '1.29', '1.30']
    Description: Kubernetes version for the EKS cluster

# ==============================================================================
# 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 (for ALB and 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'
        - Key: kubernetes.io/role/elb
          Value: '1'

  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'
        - Key: kubernetes.io/role/elb
          Value: '1'

  # Private Subnets (for EKS nodes)
  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'
        - Key: kubernetes.io/role/internal-elb
          Value: '1'

  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'
        - Key: kubernetes.io/role/internal-elb
          Value: '1'

  # Database Subnets (isolated)
  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
  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: ThreatWerk ALB - inbound HTTPS
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: '0.0.0.0/0'
          Description: HTTPS from internet (restrict to corporate CIDR in production)
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-alb-sg'

  EKSNodeSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: ThreatWerk EKS nodes
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 8080
          ToPort: 8080
          SourceSecurityGroupId: !Ref ALBSecurityGroup
          Description: Backend/frontend traffic from ALB
      SecurityGroupEgress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: '0.0.0.0/0'
          Description: AWS APIs and intel feeds
        - IpProtocol: tcp
          FromPort: 5432
          ToPort: 5432
          DestinationSecurityGroupId: !Ref RDSSecurityGroup
          Description: Database connections
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-eks-node-sg'

  # Self-referencing rule for node-to-node communication
  EKSNodeSGSelfIngress:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId: !Ref EKSNodeSecurityGroup
      IpProtocol: '-1'
      SourceSecurityGroupId: !Ref EKSNodeSecurityGroup
      Description: Node-to-node communication

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

  # ALB egress to EKS nodes
  ALBToEKSEgress:
    Type: AWS::EC2::SecurityGroupEgress
    Properties:
      GroupId: !Ref ALBSecurityGroup
      IpProtocol: tcp
      FromPort: 8080
      ToPort: 8080
      DestinationSecurityGroupId: !Ref EKSNodeSecurityGroup
      Description: Forward traffic to application pods

  # ============================================================================
  # EKS Cluster
  # ============================================================================
  EKSClusterRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub 'threatwerk-${Environment}-eks-cluster-role'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: eks.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonEKSClusterPolicy
        - arn:aws:iam::aws:policy/AmazonEKSVPCResourceController

  EKSCluster:
    Type: AWS::EKS::Cluster
    Properties:
      Name: !Sub 'threatwerk-${Environment}'
      Version: !Ref EKSVersion
      RoleArn: !GetAtt EKSClusterRole.Arn
      ResourcesVpcConfig:
        SubnetIds:
          - !Ref PrivateSubnetA
          - !Ref PrivateSubnetB
          - !Ref PublicSubnetA
          - !Ref PublicSubnetB
        SecurityGroupIds:
          - !Ref EKSNodeSecurityGroup
        EndpointPublicAccess: false
        EndpointPrivateAccess: true
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}'

  EKSNodeRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub 'threatwerk-${Environment}-eks-node-role'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy
        - arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy
        - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly

  EKSNodeGroup:
    Type: AWS::EKS::Nodegroup
    DependsOn: EKSCluster
    Properties:
      ClusterName: !Sub 'threatwerk-${Environment}'
      NodegroupName: !Sub 'threatwerk-${Environment}-nodes'
      NodeRole: !GetAtt EKSNodeRole.Arn
      Subnets:
        - !Ref PrivateSubnetA
        - !Ref PrivateSubnetB
      InstanceTypes:
        - !Ref EKSNodeInstanceType
      ScalingConfig:
        MinSize: !Ref EKSNodeCount
        MaxSize: !Ref EKSNodeCount
        DesiredSize: !Ref EKSNodeCount
      Tags:
        Name: !Sub 'threatwerk-${Environment}-node'

  # ============================================================================
  # RDS PostgreSQL
  # ============================================================================
  DBSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties:
      DBSubnetGroupDescription: ThreatWerk 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
      AllocatedStorage: 50
      StorageType: gp3
      StorageEncrypted: true
      MultiAZ: true
      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://threatwerk:CHANGE_ME@${RDSInstance.Endpoint.Address}:5432/threatwerk?sslmode=require",
          "JWT_SECRET": "CHANGE_ME",
          "ENCRYPTION_KEY": "CHANGE_ME"
        }
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-secrets'

  # ============================================================================
  # IAM - Pod Execution Role (IRSA)
  # ============================================================================
  OIDCProvider:
    Type: AWS::IAM::OIDCProvider
    DependsOn: EKSCluster
    Properties:
      Url: !GetAtt EKSCluster.OpenIdConnectIssuerUrl
      ClientIdList:
        - sts.amazonaws.com
      ThumbprintList:
        - '9e99a48a9960b14926bb7f3b02e22da2b0ab7280'
      Tags:
        - Key: Name
          Value: !Sub 'threatwerk-${Environment}-oidc'

  PodExecutionPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: !Sub 'ThreatWerkPodPolicy-${Environment}'
      Description: Minimum permissions for ThreatWerk backend pod
      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: '*'

  PodExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub 'ThreatWerkPodRole-${Environment}'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Federated: !Ref OIDCProvider
            Action: sts:AssumeRoleWithWebIdentity
            Condition:
              StringEquals:
                !Sub '${EKSCluster.OpenIdConnectIssuerUrl}:sub': 'system:serviceaccount:threatwerk:threatwerk'
      ManagedPolicyArns:
        - !Ref PodExecutionPolicy
      Tags:
        - Key: Name
          Value: !Sub 'ThreatWerkPodRole-${Environment}'

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

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

  EKSClusterName:
    Description: EKS cluster name
    Value: !Ref EKSCluster
    Export:
      Name: !Sub '${AWS::StackName}-EKSClusterName'

  EKSClusterEndpoint:
    Description: EKS cluster API endpoint
    Value: !GetAtt EKSCluster.Endpoint

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

  SecretsManagerARN:
    Description: Secrets Manager secret ARN (update values after creation)
    Value: !Ref AppSecret
    Export:
      Name: !Sub '${AWS::StackName}-SecretsARN'

  PodRoleARN:
    Description: IAM role ARN for the ThreatWerk pod (use in service account annotation)
    Value: !GetAtt PodExecutionRole.Arn
    Export:
      Name: !Sub '${AWS::StackName}-PodRoleARN'

  CertificateARN:
    Description: ACM certificate ARN (use in ingress annotation)
    Value: !Ref Certificate
    Export:
      Name: !Sub '${AWS::StackName}-CertificateARN'

  ALBSecurityGroupId:
    Description: ALB security group ID
    Value: !Ref ALBSecurityGroup

  PrivateSubnetIds:
    Description: Private subnet IDs (for reference)
    Value: !Join [',', [!Ref PrivateSubnetA, !Ref PrivateSubnetB]]

  PublicSubnetIds:
    Description: Public subnet IDs (for reference)
    Value: !Join [',', [!Ref PublicSubnetA, !Ref PublicSubnetB]]

  PostDeploymentInstructions:
    Description: Steps to complete after stack creation
    Value: >
      1. Validate the ACM certificate (add DNS CNAME record).
      2. Update Secrets Manager with proper DATABASE_URL, JWT_SECRET, ENCRYPTION_KEY.
      3. Create the threatwerk database and user in RDS.
      4. Configure kubectl: aws eks update-kubeconfig --name threatwerk-ENVIRONMENT.
      5. Install the AWS Load Balancer Controller.
      6. Deploy ThreatWerk via Helm using the output values.
