locals {
  az_count        = length(var.availability_zones)
  subnet_newbits  = ceil(log(local.az_count * 2, 2))
  subnets         = [for i in range(local.az_count * 2) : cidrsubnet(var.vpc.cidr, local.subnet_newbits, i)]
  public_subnets  = slice(local.subnets, 0, local.az_count)
  private_subnets = slice(local.subnets, local.az_count, local.az_count * 2)
}

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 6.0"

  name            = "${local.o11y_name}-vpc"
  cidr            = var.vpc.cidr
  azs             = var.availability_zones
  private_subnets = local.private_subnets
  public_subnets  = local.public_subnets

  enable_nat_gateway         = true
  single_nat_gateway         = true
  manage_default_network_acl = false
  public_subnet_tags = merge(local.o11y_shoot_tags, {
    "kubernetes.io/role/elb"                   = "1"
    "kubernetes.io/cluster/${local.o11y_name}" = "shared"
    "SubnetType"                               = "Public"
  })
  private_subnet_tags = merge(local.o11y_shoot_tags, {
    "kubernetes.io/role/internal-elb"          = "1"
    "kubernetes.io/cluster/${local.o11y_name}" = "shared"
    "SubnetType"                               = "Private"
  })

  tags = local.o11y_shoot_tags
}

data "aws_route_tables" "rts" {
  vpc_id = module.vpc.vpc_id
}

locals {
  vpc_route_table_ids = data.aws_route_tables.rts.ids
  # Safely extract the region from the BYOC ECR registry hostname. If the
  # value does not match the expected pattern, this will evaluate to null
  # instead of causing an opaque "index out of range" error.
  byoc_ecr_region = try(regex("\\.ecr\\.([a-z0-9-]+)\\.amazonaws\\.com", var.byoc_ecr)[0], null)
}

# Validate that var.byoc_ecr is a valid AWS ECR registry hostname when
# ECR PrivateLink is enabled, and fail with a clear, actionable message
# if it is not.
resource "null_resource" "validate_byoc_ecr" {
  count = var.vpc.enable_ecr_privatelink ? 1 : 0

  lifecycle {
    precondition {
      condition     = can(regex("\\.ecr\\.([a-z0-9-]+)\\.amazonaws\\.com", var.byoc_ecr)[0])
      error_message = "Invalid value for var.byoc_ecr: when var.vpc.enable_ecr_privatelink is true, byoc_ecr must be an AWS ECR registry hostname like '123456789012.dkr.ecr.eu-west-1.amazonaws.com'."
    }
  }
}

resource "aws_security_group" "ecr_endpoint" {
  count = var.vpc.enable_ecr_privatelink ? 1 : 0

  name        = "${local.o11y_name}-ecr-endpoint-sg"
  description = "Security group for ECR VPC endpoints"
  vpc_id      = module.vpc.vpc_id

  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = [var.vpc.cidr]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = merge(local.o11y_shoot_tags, {
    Name = "${local.o11y_name}-ecr-endpoint-sg"
  })
}

resource "aws_vpc_endpoint" "ecr_dkr" {
  count = var.vpc.enable_ecr_privatelink ? 1 : 0

  vpc_id              = module.vpc.vpc_id
  vpc_endpoint_type   = "Interface"
  service_name        = "com.amazonaws.${local.byoc_ecr_region}.ecr.dkr"
  service_region      = local.byoc_ecr_region
  subnet_ids          = module.vpc.private_subnets
  security_group_ids  = [aws_security_group.ecr_endpoint[0].id]
  private_dns_enabled = true

  tags = merge(local.o11y_shoot_tags, {
    Name = "${local.o11y_name}-ecr-dkr"
  })
}

resource "aws_vpc_endpoint" "ecr_api" {
  count = var.vpc.enable_ecr_privatelink ? 1 : 0

  vpc_id              = module.vpc.vpc_id
  vpc_endpoint_type   = "Interface"
  service_name        = "com.amazonaws.${local.byoc_ecr_region}.ecr.api"
  service_region      = local.byoc_ecr_region
  subnet_ids          = module.vpc.private_subnets
  security_group_ids  = [aws_security_group.ecr_endpoint[0].id]
  private_dns_enabled = true

  tags = merge(local.o11y_shoot_tags, {
    Name = "${local.o11y_name}-ecr-api"
  })
}

# Clean up orphaned Kubernetes-managed security groups before VPC deletion.
# The AWS Load Balancer Controller creates SGs for LoadBalancer services and
# ALB ingresses but does not always remove them when the EKS cluster is
# deleted (e.g. when NLBs are pre-deleted externally before LBC can react).
# These orphaned SGs cause DependencyViolation when Terraform deletes the VPC.
#
# triggers_replace references module.vpc.vpc_id so Terraform treats this
# resource as depending on the VPC; in destroy order it is therefore destroyed
# *before* module.vpc, giving us the correct pre-VPC-deletion hook.
resource "terraform_data" "vpc_k8s_sg_cleanup" {
  triggers_replace = {
    vpc_id       = module.vpc.vpc_id
    region       = var.aws_region
    cluster_name = local.o11y_name
    account_id   = data.aws_caller_identity.current.account_id
  }

  provisioner "local-exec" {
    when        = destroy
    interpreter = ["bash", "-c"]
    command     = <<-EOT
      set -euo pipefail
      VPC_ID="${self.triggers_replace.vpc_id}"
      REGION="${self.triggers_replace.region}"
      PROFILE="${self.triggers_replace.account_id}"
      CLUSTER="${self.triggers_replace.cluster_name}"

      echo "Cleaning up orphaned K8s LBC security groups for cluster $CLUSTER in VPC $VPC_ID ..."
      SG_IDS=$(aws ec2 describe-security-groups \
        --filters "Name=vpc-id,Values=$VPC_ID" \
                  "Name=tag:elbv2.k8s.aws/cluster,Values=$CLUSTER" \
        --query 'SecurityGroups[*].GroupId' \
        --output text \
        --profile "$PROFILE" --region "$REGION" 2>/dev/null || true)

      for sg_id in $SG_IDS; do
        echo "  deleting security group $sg_id ..."
        aws ec2 delete-security-group --group-id "$sg_id" \
          --profile "$PROFILE" --region "$REGION" 2>/dev/null || \
          echo "  (skipped $sg_id - may already be deleted or still has dependencies)"
      done
      echo "K8s SG cleanup done."
    EOT
  }
}