# ----------------------------------------------------------------------------------
# EFS creation logic:
# EFS is ALWAYS created by the networking stack for all scenarios.
# The networking stack handles both new VPC creation and existing VPC scenarios.
# This simplifies the architecture and eliminates circular dependencies.
# ----------------------------------------------------------------------------------

AWSTemplateFormatVersion: '2010-09-09'
Description: >
  (Root Stack) Deploys networking, an ECS Cluster, and the GraphBuild application
  with a load balancer and optional CloudFront CDN for instant HTTPS. It dynamically finds the latest ECS-optimized AMI.
  Version: v2.2.4 - Studio-only version with CloudFront support
  Marketplace Edition - No license parameters required

# ------------------------------------------------------------
# Mappings
# ------------------------------------------------------------
Mappings:
  InstanceTypeToResourceLimit:
    t3.medium:
      UiCpu: 256
      UiMemory: 512
      UiMemoryReservation: 512
      NodeCpu: 1024
      NodeMemory: 2048
      NodeMemoryReservation: 1536
    t3.large:
      UiCpu: 256
      UiMemory: 512
      UiMemoryReservation: 512
      NodeCpu: 2304
      NodeMemory: 4608
      NodeMemoryReservation: 3456

# ------------------------------------------------------------
# Parameters
# ------------------------------------------------------------
Parameters:
  ProjectName:
    Type: String
    Default: 'GraphBuild'
    Description: A project name to be used for naming resources.

  AWSRegion:
    Type: String
    Default: 'us-east-1'
    Description: >-
      AWS Region where the stack is deployed. This will be passed as an environment variable to the Node app.
      The Node app requires this to interact with AWS services in the correct region.
    AllowedValues:
      - 'us-east-1'
      - 'us-east-2'
      - 'us-west-1'
      - 'us-west-2'
      - 'eu-west-1'
      - 'eu-west-2'
      - 'eu-central-1'
      - 'ap-southeast-1'
      - 'ap-southeast-2'
      - 'ap-northeast-1'
    ConstraintDescription: Must be a valid AWS region


  EnableCloudFront:
    Type: String
    Default: 'false'
    AllowedValues: ['true', 'false']
    Description: >-
      Enable CloudFront CDN for instant HTTPS with free AWS-managed certificate (*.cloudfront.net domain).
      Provides global edge caching and DDoS protection. Can add custom domain later.

  InstanceType:
    Type: String
    Default: 't3.medium'
    Description: The EC2 instance type for the ECS container instances. Container resources are automatically scaled based on instance type.
    AllowedValues:
      # T3 Instances (General Purpose, Burstable)
      - t3.medium
      - t3.large
    ConstraintDescription: Must be a valid EC2 instance type.

  EnableSSH:
    Type: String
    Default: 'false'
    AllowedValues: ['true', 'false']
    Description: Enable SSH access to EC2 instances?

  KeyName:
    Type: String
    Default: ''
    Description: Enter an existing EC2 Key Pair name for SSH access (only required if EnableSSH is true).

  SSHLocation:
    Type: String
    Default: '0.0.0.0/0'
    Description: >-
      The IP address range that can be used to SSH to the EC2 instances.
      WARNING: Default '0.0.0.0/0' allows SSH from anywhere on the internet. 
      For production, restrict to your IP address (e.g., '203.0.113.0/32').
    AllowedPattern: '(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})'
    ConstraintDescription: Must be a valid IP CIDR range of the form x.x.x.x/x.

  VpcCreationChoice:
    Type: String
    Default: 'Create new VPC'
    Description: Select whether to create a new VPC or use an existing one.
    AllowedValues:
      - 'Create new VPC'
      - 'Use existing VPC'

  # --- Parameters for using an EXISTING VPC ---
  VpcId:
    Type: String
    Default: ''
    Description: >-
      (Required if using existing VPC) Select an existing VPC to deploy the cluster into.

  PublicSubnetIds:
    Type: CommaDelimitedList
    Default: ''
    Description: >-
      (Required if using existing VPC) Select at least two PUBLIC subnet IDs for the load balancer.
      IMPORTANT: You MUST include a public subnet in the SAME availability zone as your private subnet (specified in PrivateSubnetAvailabilityZone).
      The ALB will only route traffic to ECS instances in availability zones covered by these public subnets.
      Example: If your private subnet is in eu-west-2b, one of your public subnets must also be in eu-west-2b.

  PrivateSubnetId:
    Type: CommaDelimitedList
    Default: ''
    Description: >-
      (Required if using existing VPC) Select ONE PRIVATE subnet ID for the cluster instances.
      This subnet must have a route to a NAT Gateway for outbound internet access.
      Note: Only the first subnet will be used if multiple are provided.

  PrivateSubnetAvailabilityZone:
    Type: String
    Default: ''
    Description: >-
      (Required if using existing VPC) The availability zone of your private subnet (e.g., eu-west-2b).
      This must match the AZ of the subnet in PrivateSubnetId.
      CRITICAL: Ensure you have included a public subnet in this SAME availability zone in the PublicSubnetIds parameter,
      otherwise the Application Load Balancer will not be able to route traffic to your ECS instances and health checks will fail.
      For new VPC creation, this parameter is ignored and auto-detected.
    AllowedPattern: '^([a-z]{2}-[a-z]+-[0-9]+[a-z])?$'
    ConstraintDescription: Must be a valid availability zone (e.g., us-east-1a) or leave empty for auto-detection

  # --- Application Parameters ---


# ------------------------------------------------------------
# Conditions
# ------------------------------------------------------------
Conditions:
  CreateVPCResources: !Equals [!Ref VpcCreationChoice, 'Create new VPC']
  UseExistingVPC: !Not [!Equals [!Ref VpcId, '']]
  UseSSHKey: !Equals [!Ref EnableSSH, 'true']
  HasPrivateSubnetAZ: !Not [!Equals [!Ref PrivateSubnetAvailabilityZone, '']]

# ------------------------------------------------------------
# Resources
# ------------------------------------------------------------
Resources:
  # --- Nested Networking Stack (Only created for new VPC) ---
  NetworkingStack:
    Type: AWS::CloudFormation::Stack
    Condition: CreateVPCResources
    Properties:
      TemplateURL: 'https://s3.amazonaws.com/graphbuild-cf-templates/studio-m/marketplace-networking-stack-v2.2.4.yaml'
      Parameters:
        ProjectName: !Ref ProjectName
      TimeoutInMinutes: 15

  # --- Security Groups ---
  ALBSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !If [CreateVPCResources, !GetAtt NetworkingStack.Outputs.VpcId, !Ref VpcId]
      GroupDescription: 'Allow inbound HTTP from anywhere'
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-ALB-SG'

  ECSHostSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !If [CreateVPCResources, !GetAtt NetworkingStack.Outputs.VpcId, !Ref VpcId]
      GroupDescription: !If [UseSSHKey, 'Allow traffic from ALB and SSH', 'Allow traffic from ALB']
      SecurityGroupIngress: !If 
        - UseSSHKey
        - - IpProtocol: tcp
            FromPort: 0 # Allow all ports from the ALB for simplicity with multiple host ports
            ToPort: 65535
            SourceSecurityGroupId: !GetAtt ALBSecurityGroup.GroupId
          - IpProtocol: tcp
            FromPort: 22
            ToPort: 22
            CidrIp: !Ref SSHLocation
        - - IpProtocol: tcp
            FromPort: 0 # Allow all ports from the ALB for simplicity with multiple host ports
            ToPort: 65535
            SourceSecurityGroupId: !GetAtt ALBSecurityGroup.GroupId
      SecurityGroupEgress:
        - IpProtocol: tcp
          FromPort: 2049
          ToPort: 2049
          CidrIp: 10.0.0.0/8
          Description: Allow outbound NFS traffic to EFS
        - IpProtocol: -1
          CidrIp: 0.0.0.0/0
          Description: All other outbound traffic
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-ECS-Host-SG'


  # --- IAM Roles ---
  EC2InstanceRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub '${ProjectName}-EC2InstanceRole'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal: { Service: ec2.amazonaws.com }
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role

  EC2InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: /
      Roles: [!Ref EC2InstanceRole]

  # --- ECS Cluster ---
  ECSCluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: !Sub '${ProjectName}-Cluster'

  # --- Launch Template ---
  ECSLaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateName: !Sub '${ProjectName}-LaunchTemplate'
      LaunchTemplateData:
        ImageId: '{{resolve:ssm:/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id}}'
        InstanceType: !Ref InstanceType
        KeyName: !If [UseSSHKey, !Ref KeyName, !Ref 'AWS::NoValue']
        SecurityGroupIds:
          - !Ref ECSHostSecurityGroup
        IamInstanceProfile:
          Arn: !GetAtt EC2InstanceProfile.Arn
        UserData:
          Fn::Base64:
            Fn::Sub: |
              #!/bin/bash
              echo ECS_CLUSTER=${ECSCluster} >> /etc/ecs/ecs.config

  # --- Auto Scaling Group ---
  ECSAutoScalingGroup:
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties:
      AutoScalingGroupName: !Sub '${ProjectName}-ASG'
      VPCZoneIdentifier:
        - !If 
          - CreateVPCResources
          - !GetAtt NetworkingStack.Outputs.PrivateSubnetIds
          - !Select [0, !Ref PrivateSubnetId]
      LaunchTemplate:
        LaunchTemplateId: !Ref ECSLaunchTemplate
        Version: !GetAtt ECSLaunchTemplate.LatestVersionNumber
      DesiredCapacity: 1
      MinSize: 1
      MaxSize: 1
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-ECS-Host'
          PropagateAtLaunch: true

  # --- Nested Application Stack ---
  ApplicationStack:
    Type: AWS::CloudFormation::Stack
    DependsOn: ECSAutoScalingGroup # Ensure cluster instances are ready before deploying service
    Properties:
      TemplateURL: 'https://s3.amazonaws.com/graphbuild-cf-templates/studio-m/marketplace-application-stack-v2.2.4.yaml'
      Parameters:
        ClusterName: !GetAtt ECSCluster.Arn
        VpcId: !If [CreateVPCResources, !GetAtt NetworkingStack.Outputs.VpcId, !Ref VpcId]
        PublicSubnetIds: !If [CreateVPCResources, !GetAtt NetworkingStack.Outputs.PublicSubnetIds, !Join [',', !Ref PublicSubnetIds]]
        ALBSecurityGroup: !GetAtt ALBSecurityGroup.GroupId
        RootStackName: !Ref 'AWS::StackName'
        PrivateSubnetId: !If 
          - CreateVPCResources
          - !Select [0, !Split [',', !GetAtt NetworkingStack.Outputs.PrivateSubnetIds]]
          - !Select [0, !Ref PrivateSubnetId]
        ECSHostSecurityGroup: !Ref ECSHostSecurityGroup
        InstanceType: !Ref InstanceType
        AWSRegion: !Ref AWSRegion
        EnableCloudFront: !Ref EnableCloudFront

# ------------------------------------------------------------
# Outputs
# ------------------------------------------------------------
Outputs:
  ClusterName:
    Description: The name of the ECS Cluster
    Value: !Ref ECSCluster
  ApplicationURL:
    Description: The URL of the deployed application
    Value: !GetAtt ApplicationStack.Outputs.LoadBalancerDNS
  StudioAdminPasswordSecret:
    Description: Link to the AWS Secrets Manager secret containing the Studio admin password
    Value: !GetAtt ApplicationStack.Outputs.StudioAdminPasswordSecret