AWSTemplateFormatVersion: '2010-09-09'
Description: 'Qdrant Cluster on ECS Fargate with 3 nodes, replication, and Network Load Balancer'

Parameters:
  EnvironmentName:
    Type: String
    Default: dev
    Description: Environment name (e.g., dev, staging, production)
    AllowedValues:
      - dev
      - staging
      - productionx

  VpcCIDR:
    Type: String
    Default: 10.0.0.0/16
    Description: CIDR block for VPC

  QdrantImageTag:
    Type: String
    Default: latest
    Description: Qdrant Docker image tag

  TaskCpu:
    Type: String
    Default: '2048'
    Description: Task CPU units (1024 = 1 vCPU)
    AllowedValues:
      - '1024'
      - '2048'
      - '4096'

  TaskMemory:
    Type: String
    Default: '8192'
    Description: Task memory in MB
    AllowedValues:
      - '4096'
      - '8192'
      - '16384'

  DesiredCount:
    Type: Number
    Default: 1
    MinValue: 1
    MaxValue: 10
    Description: Number of Qdrant tasks (start with 1, then scale to 3)

Resources:
  # ==================== VPC and Networking ====================

  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VpcCIDR
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-qdrant-vpc

  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-qdrant-igw

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

  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select [0, !GetAZs '']
      CidrBlock: !Select [0, !Cidr [!Ref VpcCIDR, 6, 8]]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-qdrant-public-subnet-1

  PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select [1, !GetAZs '']
      CidrBlock: !Select [1, !Cidr [!Ref VpcCIDR, 6, 8]]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-qdrant-public-subnet-2

  PublicSubnet3:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select [2, !GetAZs '']
      CidrBlock: !Select [2, !Cidr [!Ref VpcCIDR, 6, 8]]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-qdrant-public-subnet-3

  PrivateSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select [0, !GetAZs '']
      CidrBlock: !Select [3, !Cidr [!Ref VpcCIDR, 6, 8]]
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-qdrant-private-subnet-1

  PrivateSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select [1, !GetAZs '']
      CidrBlock: !Select [4, !Cidr [!Ref VpcCIDR, 6, 8]]
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-qdrant-private-subnet-2

  PrivateSubnet3:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select [2, !GetAZs '']
      CidrBlock: !Select [5, !Cidr [!Ref VpcCIDR, 6, 8]]
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-qdrant-private-subnet-3

  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-qdrant-public-routes

  DefaultPublicRoute:
    Type: AWS::EC2::Route
    DependsOn: InternetGatewayAttachment
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  PublicSubnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet1

  PublicSubnet2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet2

  PublicSubnet3RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet3

  # NAT Gateways for private subnets
  NatGateway1EIP:
    Type: AWS::EC2::EIP
    DependsOn: InternetGatewayAttachment
    Properties:
      Domain: vpc

  NatGateway1:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt NatGateway1EIP.AllocationId
      SubnetId: !Ref PublicSubnet1

  PrivateRouteTable1:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-qdrant-private-routes-1

  DefaultPrivateRoute1:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTable1
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatGateway1

  PrivateSubnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTable1
      SubnetId: !Ref PrivateSubnet1

  PrivateSubnet2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTable1
      SubnetId: !Ref PrivateSubnet2

  PrivateSubnet3RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTable1
      SubnetId: !Ref PrivateSubnet3

  # ==================== Security Groups ====================

  LoadBalancerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub ${EnvironmentName}-qdrant-alb-sg
      GroupDescription: Security group for Qdrant load balancer
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 6334
          ToPort: 6334
          CidrIp: 0.0.0.0/0
          Description: gRPC access from internet
        - IpProtocol: tcp
          FromPort: 6333
          ToPort: 6333
          CidrIp: 0.0.0.0/0
          Description: HTTP API access from internet
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-qdrant-alb-sg

  QdrantServiceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub ${EnvironmentName}-qdrant-service-sg
      GroupDescription: Security group for Qdrant service
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-qdrant-service-sg

  QdrantServiceSecurityGroupIngressHTTP:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId: !Ref QdrantServiceSecurityGroup
      IpProtocol: tcp
      FromPort: 6333
      ToPort: 6333
      CidrIp: !Ref VpcCIDR
      Description: HTTP from VPC (NLB health checks and traffic)

  QdrantServiceSecurityGroupIngressGRPC:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId: !Ref QdrantServiceSecurityGroup
      IpProtocol: tcp
      FromPort: 6334
      ToPort: 6334
      CidrIp: !Ref VpcCIDR
      Description: gRPC from VPC (NLB health checks and traffic)

  QdrantServiceSecurityGroupIngressP2P:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId: !Ref QdrantServiceSecurityGroup
      IpProtocol: tcp
      FromPort: 6335
      ToPort: 6335
      SourceSecurityGroupId: !Ref QdrantServiceSecurityGroup
      Description: P2P cluster communication

  # EFS Security Group removed - not using EFS for clustering

  # ==================== EFS File System ====================

  # EFS removed - using ephemeral Fargate storage for clustering
  # Qdrant clustering requires independent storage per node
  # Data is replicated across nodes (replication_factor=3)

  # ==================== ECS Cluster ====================

  ECSCluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: !Sub ${EnvironmentName}-qdrant-cluster
      CapacityProviders:
        - FARGATE
        - FARGATE_SPOT
      DefaultCapacityProviderStrategy:
        - CapacityProvider: FARGATE
          Weight: 1
      Configuration:
        ExecuteCommandConfiguration:
          Logging: DEFAULT
      ClusterSettings:
        - Name: containerInsights
          Value: enabled

  # ==================== CloudWatch Logs ====================

  QdrantLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub /ecs/${EnvironmentName}-qdrant
      RetentionInDays: 7

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

  ECSTaskExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service: ecs-tasks.amazonaws.com
            Action: 'sts:AssumeRole'
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy

  ECSTaskRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service: ecs-tasks.amazonaws.com
            Action: 'sts:AssumeRole'
      Policies:
        - PolicyName: QdrantTaskPolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - cloudwatch:PutMetricData
                Resource: '*'
              - Effect: Allow
                Action:
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource: !GetAtt QdrantLogGroup.Arn

  # ==================== ECS Task Definition ====================

  QdrantTaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: !Sub ${EnvironmentName}-qdrant
      NetworkMode: awsvpc
      RequiresCompatibilities:
        - FARGATE
      RuntimePlatform:
        CpuArchitecture: ARM64
        OperatingSystemFamily: LINUX
      Cpu: !Ref TaskCpu
      Memory: !Ref TaskMemory
      ExecutionRoleArn: !Ref ECSTaskExecutionRole
      TaskRoleArn: !Ref ECSTaskRole
      ContainerDefinitions:
        - Name: qdrant
          Image: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/qdrant-cluster:cluster-v3
          Essential: true
          PortMappings:
            - ContainerPort: 6333
              Protocol: tcp
              Name: http
            - ContainerPort: 6334
              Protocol: tcp
              Name: grpc
            - ContainerPort: 6335
              Protocol: tcp
              Name: p2p
          Environment:
            - Name: QDRANT__CLUSTER__ENABLED
              Value: 'true'
            - Name: QDRANT__CLUSTER__P2P__PORT
              Value: '6335'
            - Name: QDRANT__INIT__FORCE_CLUSTER_INIT
              Value: 'true'
            - Name: QDRANT__SERVICE__HTTP_PORT
              Value: '6333'
            - Name: QDRANT__SERVICE__GRPC_PORT
              Value: '6334'
            - Name: QDRANT__SERVICE__HOST
              Value: '0.0.0.0'
            - Name: QDRANT__LOG_LEVEL
              Value: 'INFO'
          LogConfiguration:
            LogDriver: awslogs
            Options:
              awslogs-group: !Ref QdrantLogGroup
              awslogs-region: !Ref AWS::Region
              awslogs-stream-prefix: qdrant
          HealthCheck:
            Command:
              - CMD-SHELL
              - wget --no-verbose --tries=1 -O /dev/null http://localhost:6333/ || exit 1
            Interval: 30
            Timeout: 5
            Retries: 3
            StartPeriod: 60

  # ==================== Network Load Balancer ====================

  NetworkLoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: !Sub ${EnvironmentName}-qdrant-nlb
      Type: network
      Scheme: internet-facing
      IpAddressType: ipv4
      Subnets:
        - !Ref PublicSubnet1
        - !Ref PublicSubnet2
        - !Ref PublicSubnet3
      LoadBalancerAttributes:
        - Key: load_balancing.cross_zone.enabled
          Value: 'true'
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-qdrant-nlb

  # Target Group for gRPC (6334)
  QdrantGRPCTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Sub ${EnvironmentName}-qdrant-grpc-tg
      Port: 6334
      Protocol: TCP
      VpcId: !Ref VPC
      TargetType: ip
      HealthCheckEnabled: true
      HealthCheckProtocol: HTTP
      HealthCheckPort: '6333'
      HealthCheckPath: /
      HealthCheckIntervalSeconds: 30
      HealthyThresholdCount: 2
      UnhealthyThresholdCount: 2
      TargetGroupAttributes:
        - Key: deregistration_delay.timeout_seconds
          Value: '30'
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-qdrant-grpc-tg

  # Target Group for HTTP (6333)
  QdrantHTTPTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Sub ${EnvironmentName}-qdrant-http-tg
      Port: 6333
      Protocol: TCP
      VpcId: !Ref VPC
      TargetType: ip
      HealthCheckEnabled: true
      HealthCheckProtocol: HTTP
      HealthCheckPort: '6333'
      HealthCheckPath: /
      HealthCheckIntervalSeconds: 30
      HealthyThresholdCount: 2
      UnhealthyThresholdCount: 2
      TargetGroupAttributes:
        - Key: deregistration_delay.timeout_seconds
          Value: '30'
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-qdrant-http-tg

  # Listener for gRPC (6334)
  QdrantGRPCListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref NetworkLoadBalancer
      Port: 6334
      Protocol: TCP
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref QdrantGRPCTargetGroup

  # Listener for HTTP (6333)
  QdrantHTTPListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref NetworkLoadBalancer
      Port: 6333
      Protocol: TCP
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref QdrantHTTPTargetGroup

  # ==================== Service Discovery ====================

  ServiceDiscoveryNamespace:
    Type: AWS::ServiceDiscovery::PrivateDnsNamespace
    Properties:
      Name: !Sub '${EnvironmentName}-qdrant.local'
      Vpc: !Ref VPC
      Description: Service discovery namespace for Qdrant cluster

  ServiceDiscoveryService:
    Type: AWS::ServiceDiscovery::Service
    Properties:
      Name: qdrant
      DnsConfig:
        NamespaceId: !Ref ServiceDiscoveryNamespace
        DnsRecords:
          - Type: A
            TTL: 10
      HealthCheckCustomConfig:
        FailureThreshold: 1

  # ==================== ECS Service ====================

  QdrantService:
    Type: AWS::ECS::Service
    DependsOn:
      - QdrantGRPCListener
      - QdrantHTTPListener
    Properties:
      ServiceName: !Sub ${EnvironmentName}-qdrant-service
      Cluster: !Ref ECSCluster
      TaskDefinition: !Ref QdrantTaskDefinition
      DesiredCount: !Ref DesiredCount
      LaunchType: FARGATE
      PlatformVersion: LATEST
      NetworkConfiguration:
        AwsvpcConfiguration:
          AssignPublicIp: DISABLED
          SecurityGroups:
            - !Ref QdrantServiceSecurityGroup
          Subnets:
            - !Ref PrivateSubnet1
            - !Ref PrivateSubnet2
            - !Ref PrivateSubnet3
      ServiceRegistries:
        - RegistryArn: !GetAtt ServiceDiscoveryService.Arn
      LoadBalancers:
        - TargetGroupArn: !Ref QdrantGRPCTargetGroup
          ContainerName: qdrant
          ContainerPort: 6334
        - TargetGroupArn: !Ref QdrantHTTPTargetGroup
          ContainerName: qdrant
          ContainerPort: 6333
      HealthCheckGracePeriodSeconds: 180
      DeploymentConfiguration:
        MaximumPercent: 200
        MinimumHealthyPercent: 100
        DeploymentCircuitBreaker:
          Enable: true
          Rollback: true
      EnableECSManagedTags: true
      PropagateTags: SERVICE
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-qdrant-service

  # ==================== Auto Scaling ====================

  ServiceScalingTarget:
    Type: AWS::ApplicationAutoScaling::ScalableTarget
    Properties:
      MaxCapacity: 10
      MinCapacity: 1
      ResourceId: !Sub service/${ECSCluster}/${QdrantService.Name}
      RoleARN: !Sub arn:aws:iam::${AWS::AccountId}:role/aws-service-role/ecs.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_ECSService
      ScalableDimension: ecs:service:DesiredCount
      ServiceNamespace: ecs

  ServiceScalingPolicyCPU:
    Type: AWS::ApplicationAutoScaling::ScalingPolicy
    Properties:
      PolicyName: !Sub ${EnvironmentName}-qdrant-cpu-scaling
      PolicyType: TargetTrackingScaling
      ScalingTargetId: !Ref ServiceScalingTarget
      TargetTrackingScalingPolicyConfiguration:
        PredefinedMetricSpecification:
          PredefinedMetricType: ECSServiceAverageCPUUtilization
        TargetValue: 70.0
        ScaleInCooldown: 300
        ScaleOutCooldown: 60

  ServiceScalingPolicyMemory:
    Type: AWS::ApplicationAutoScaling::ScalingPolicy
    Properties:
      PolicyName: !Sub ${EnvironmentName}-qdrant-memory-scaling
      PolicyType: TargetTrackingScaling
      ScalingTargetId: !Ref ServiceScalingTarget
      TargetTrackingScalingPolicyConfiguration:
        PredefinedMetricSpecification:
          PredefinedMetricType: ECSServiceAverageMemoryUtilization
        TargetValue: 80.0
        ScaleInCooldown: 300
        ScaleOutCooldown: 60

  # ==================== CloudWatch Alarms ====================

  HighCPUAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: !Sub ${EnvironmentName}-qdrant-high-cpu
      AlarmDescription: Qdrant service CPU utilization is too high
      MetricName: CPUUtilization
      Namespace: AWS/ECS
      Statistic: Average
      Period: 300
      EvaluationPeriods: 2
      Threshold: 85
      ComparisonOperator: GreaterThanThreshold
      Dimensions:
        - Name: ServiceName
          Value: !GetAtt QdrantService.Name
        - Name: ClusterName
          Value: !Ref ECSCluster

  UnhealthyTargetsAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: !Sub ${EnvironmentName}-qdrant-unhealthy-targets
      AlarmDescription: Qdrant has unhealthy targets
      MetricName: UnHealthyHostCount
      Namespace: AWS/NetworkELB
      Statistic: Average
      Period: 60
      EvaluationPeriods: 2
      Threshold: 1
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: TargetGroup
          Value: !GetAtt QdrantGRPCTargetGroup.TargetGroupFullName
        - Name: LoadBalancer
          Value: !GetAtt NetworkLoadBalancer.LoadBalancerFullName

# ==================== Outputs ====================

Outputs:
  ClusterName:
    Description: ECS Cluster Name
    Value: !Ref ECSCluster
    Export:
      Name: !Sub ${EnvironmentName}-QdrantClusterName

  ServiceName:
    Description: ECS Service Name
    Value: !GetAtt QdrantService.Name
    Export:
      Name: !Sub ${EnvironmentName}-QdrantServiceName

  LoadBalancerDNS:
    Description: Network Load Balancer DNS Name
    Value: !GetAtt NetworkLoadBalancer.DNSName
    Export:
      Name: !Sub ${EnvironmentName}-QdrantNLBDNS

  QdrantGRPCEndpoint:
    Description: Qdrant gRPC endpoint (for your Rust application)
    Value: !Sub http://${NetworkLoadBalancer.DNSName}:6334
    Export:
      Name: !Sub ${EnvironmentName}-QdrantGRPCEndpoint

  QdrantHTTPEndpoint:
    Description: Qdrant HTTP API endpoint
    Value: !Sub http://${NetworkLoadBalancer.DNSName}:6333
    Export:
      Name: !Sub ${EnvironmentName}-QdrantHTTPEndpoint

  QdrantDashboard:
    Description: Qdrant Web Dashboard
    Value: !Sub http://${NetworkLoadBalancer.DNSName}:6333/dashboard

  VPCId:
    Description: VPC ID
    Value: !Ref VPC
    Export:
      Name: !Sub ${EnvironmentName}-QdrantVPCId

  SecurityGroupId:
    Description: Qdrant Service Security Group ID
    Value: !Ref QdrantServiceSecurityGroup
    Export:
      Name: !Sub ${EnvironmentName}-QdrantSecurityGroupId
