################
# API Gateway — unified external entry point for ConnectRPC consumers
#
# Routes requests by proto package prefix to existing service ALBs.
# JWT authorizer validates Auth0 tokens at the edge.
# Internal service-to-service traffic is unaffected (stays direct).
################

# Skip API Gateway for ephemeral environments (PR instances).
# Ephemeral envs don't need external API access — they're for testing.
locals {
  create_api_gateway = !local.is_ephemeral

  # ALB domain names — used for TLS server name verification through VPC Link.
  # All ALBs use the *.theorchard.io wildcard cert.
  server_alb_domain   = "${var.environment}-${var.service_name}.theorchard.io"
  platform_alb_domain = "${var.environment}-${local.platform_service_name}.theorchard.io"
  runner_alb_domain   = "${var.environment}-${local.runner_service_name}.theorchard.io"
  search_alb_domain   = "${var.environment}-${local.search_service_name}.theorchard.io"
}

################
# ACM Certificate (legacy)
#
# Previously used for api.coda.theorchard.io (two-level subdomain not covered
# by the *.theorchard.io wildcard). Now superseded by the wildcard cert after
# moving to qa-coda-api.theorchard.io. Kept here so Terraform doesn't try to
# destroy it — delete the cert and these resources separately.
################

resource "aws_acm_certificate" "api_gateway" {
  count             = local.create_api_gateway ? 1 : 0
  domain_name       = "api.coda.theorchard.io"
  validation_method = "DNS"

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_route53_record" "api_gateway_cert_validation" {
  for_each = local.create_api_gateway ? {
    for dvo in aws_acm_certificate.api_gateway[0].domain_validation_options : dvo.domain_name => {
      name   = dvo.resource_record_name
      type   = dvo.resource_record_type
      record = dvo.resource_record_value
    }
  } : {}

  provider = aws.networking
  zone_id  = data.aws_route53_zone.theorchard_io[0].zone_id
  name     = each.value.name
  type     = each.value.type
  records  = [each.value.record]
  ttl      = 60
}

resource "aws_acm_certificate_validation" "api_gateway" {
  count                   = local.create_api_gateway ? 1 : 0
  certificate_arn         = aws_acm_certificate.api_gateway[0].arn
  validation_record_fqdns = [for r in aws_route53_record.api_gateway_cert_validation : r.fqdn]
}

################
# HTTP API
#
# Route53 zone lookup reused from ecs_ec2.tf:
#   data.aws_route53_zone.theorchard_io[0]
################

resource "aws_apigatewayv2_api" "coda" {
  count         = local.create_api_gateway ? 1 : 0
  name          = "${var.environment}-${var.service_name}-api"
  protocol_type = "HTTP"
  description   = "Unified API Gateway for Coda ConnectRPC services"
}

################
# Access Logging (CloudWatch)
#
# CKV_AWS_76 requires access logging on API Gateway stages.
# HTTP API v2 has built-in CloudWatch permissions — no IAM role needed.
################

resource "aws_cloudwatch_log_group" "api_gateway" {
  count             = local.create_api_gateway ? 1 : 0
  name              = "/aws/apigateway/${var.environment}-${var.service_name}-api"
  retention_in_days = 365
}

resource "aws_apigatewayv2_stage" "default" {
  count       = local.create_api_gateway ? 1 : 0
  api_id      = aws_apigatewayv2_api.coda[0].id
  name        = "$default"
  auto_deploy = true

  access_log_settings {
    destination_arn = aws_cloudwatch_log_group.api_gateway[0].arn
    format = jsonencode({
      requestId        = "$context.requestId"
      ip               = "$context.identity.sourceIp"
      requestTime      = "$context.requestTime"
      httpMethod       = "$context.httpMethod"
      routeKey         = "$context.routeKey"
      status           = "$context.status"
      protocol         = "$context.protocol"
      responseLength   = "$context.responseLength"
      integrationError = "$context.integrationErrorMessage"
    })
  }
}

################
# JWT Authorizer (Auth0)
################

resource "aws_apigatewayv2_authorizer" "auth0" {
  count            = local.create_api_gateway ? 1 : 0
  api_id           = aws_apigatewayv2_api.coda[0].id
  authorizer_type  = "JWT"
  name             = "auth0"
  identity_sources = ["$request.header.Authorization"]

  jwt_configuration {
    audience = [var.auth0_audience]
    issuer   = "https://${var.auth0_domain}/"
  }
}

################
# VPC Link — connects API Gateway to internal ALBs
#
# All service ALBs are internal (private subnets). API Gateway HTTP API
# can't reach them without a VPC Link.
#
# Pattern reference: qa/lambda-fan-response/email_campaigns_http_proxy_endpoint.tf
# uses the same aws_apigatewayv2_vpc_link approach with security groups + subnets.
# Egress scoped to specific ALB SGs (not 0.0.0.0/0) per that pattern.
################

resource "aws_security_group" "api_gateway_vpc_link" {
  count       = local.create_api_gateway ? 1 : 0
  name        = "${var.environment}-${var.service_name}-api-gateway-vpc-link"
  description = "API Gateway VPC Link - egress to internal ALBs on port 443"
  vpc_id      = module.vpc_info.vpc_id
}

# Egress rules — scoped to specific ALB security groups (least privilege).
resource "aws_security_group_rule" "vpc_link_egress_to_server" {
  count                    = local.create_api_gateway ? 1 : 0
  type                     = "egress"
  from_port                = 443
  to_port                  = 443
  protocol                 = "tcp"
  source_security_group_id = module.ows_coda_fargate_environment.fargate_load_balancer_security_group_id
  security_group_id        = aws_security_group.api_gateway_vpc_link[0].id
  description              = "HTTPS to server ALB"
}

resource "aws_security_group_rule" "vpc_link_egress_to_platform" {
  count                    = local.create_api_gateway ? 1 : 0
  type                     = "egress"
  from_port                = 443
  to_port                  = 443
  protocol                 = "tcp"
  source_security_group_id = module.platform_fargate[0].fargate_load_balancer_security_group_id
  security_group_id        = aws_security_group.api_gateway_vpc_link[0].id
  description              = "HTTPS to platform ALB"
}

resource "aws_security_group_rule" "vpc_link_egress_to_runner" {
  count                    = local.create_api_gateway ? 1 : 0
  type                     = "egress"
  from_port                = 443
  to_port                  = 443
  protocol                 = "tcp"
  source_security_group_id = module.runner_fargate[0].fargate_load_balancer_security_group_id
  security_group_id        = aws_security_group.api_gateway_vpc_link[0].id
  description              = "HTTPS to runner ALB"
}

resource "aws_security_group_rule" "vpc_link_egress_to_search" {
  count                    = local.create_api_gateway ? 1 : 0
  type                     = "egress"
  from_port                = 443
  to_port                  = 443
  protocol                 = "tcp"
  source_security_group_id = aws_security_group.search_alb[0].id
  security_group_id        = aws_security_group.api_gateway_vpc_link[0].id
  description              = "HTTPS to search ALB"
}

# Allow traffic from VPC Link into the server ALB.
# The Fargate module's ALB SG is exposed via fargate_load_balancer_security_group_id.
resource "aws_security_group_rule" "vpc_link_to_server_alb" {
  count                    = local.create_api_gateway ? 1 : 0
  type                     = "ingress"
  from_port                = 443
  to_port                  = 443
  protocol                 = "tcp"
  source_security_group_id = aws_security_group.api_gateway_vpc_link[0].id
  security_group_id        = module.ows_coda_fargate_environment.fargate_load_balancer_security_group_id
  description              = "API Gateway VPC Link to server ALB"
}

# Allow traffic from VPC Link into the platform ALB.
resource "aws_security_group_rule" "vpc_link_to_platform_alb" {
  count                    = local.create_api_gateway ? 1 : 0
  type                     = "ingress"
  from_port                = 443
  to_port                  = 443
  protocol                 = "tcp"
  source_security_group_id = aws_security_group.api_gateway_vpc_link[0].id
  security_group_id        = module.platform_fargate[0].fargate_load_balancer_security_group_id
  description              = "API Gateway VPC Link to platform ALB"
}

# Allow traffic from VPC Link into the runner ALB.
resource "aws_security_group_rule" "vpc_link_to_runner_alb" {
  count                    = local.create_api_gateway ? 1 : 0
  type                     = "ingress"
  from_port                = 443
  to_port                  = 443
  protocol                 = "tcp"
  source_security_group_id = aws_security_group.api_gateway_vpc_link[0].id
  security_group_id        = module.runner_fargate[0].fargate_load_balancer_security_group_id
  description              = "API Gateway VPC Link to runner ALB"
}

# Allow traffic from VPC Link into the search ALB.
# Search ALB was made internal — external access now goes through the gateway.
resource "aws_security_group_rule" "vpc_link_to_search_alb" {
  count                    = local.create_api_gateway ? 1 : 0
  type                     = "ingress"
  from_port                = 443
  to_port                  = 443
  protocol                 = "tcp"
  source_security_group_id = aws_security_group.api_gateway_vpc_link[0].id
  security_group_id        = aws_security_group.search_alb[0].id
  description              = "API Gateway VPC Link to search ALB"
}

resource "aws_apigatewayv2_vpc_link" "coda" {
  count              = local.create_api_gateway ? 1 : 0
  name               = "${var.environment}-${var.service_name}-api-vpc-link"
  security_group_ids = [aws_security_group.api_gateway_vpc_link[0].id]
  subnet_ids         = module.vpc_info.default_private_subnet_ids

  lifecycle {
    create_before_destroy = true
  }
}

################
# Integrations — one per target ALB
#
# All ALBs are internal (private subnets). Each integration uses VPC Link
# with the ALB listener ARN as integration_uri (required for HTTP API v2
# private integrations).
#
# tls_config is required — without it, API Gateway sends plain HTTP through
# the VPC Link, and the ALB HTTPS listeners reject with 400.
################

resource "aws_apigatewayv2_integration" "server" {
  count              = local.create_api_gateway ? 1 : 0
  api_id             = aws_apigatewayv2_api.coda[0].id
  integration_type   = "HTTP_PROXY"
  integration_method = "ANY"
  connection_type    = "VPC_LINK"
  connection_id      = aws_apigatewayv2_vpc_link.coda[0].id
  integration_uri    = module.ows_coda_fargate_environment.fargate_load_balancer_https_listener_arn

  tls_config {
    server_name_to_verify = local.server_alb_domain
  }
}

resource "aws_apigatewayv2_integration" "platform" {
  count              = local.create_api_gateway ? 1 : 0
  api_id             = aws_apigatewayv2_api.coda[0].id
  integration_type   = "HTTP_PROXY"
  integration_method = "ANY"
  connection_type    = "VPC_LINK"
  connection_id      = aws_apigatewayv2_vpc_link.coda[0].id
  integration_uri    = module.platform_fargate[0].fargate_load_balancer_https_listener_arn

  tls_config {
    server_name_to_verify = local.platform_alb_domain
  }
}

resource "aws_apigatewayv2_integration" "search" {
  count              = local.create_api_gateway ? 1 : 0
  api_id             = aws_apigatewayv2_api.coda[0].id
  integration_type   = "HTTP_PROXY"
  integration_method = "ANY"
  connection_type    = "VPC_LINK"
  connection_id      = aws_apigatewayv2_vpc_link.coda[0].id
  integration_uri    = aws_lb_listener.search_https[0].arn

  tls_config {
    server_name_to_verify = local.search_alb_domain
  }
}

resource "aws_apigatewayv2_integration" "runner" {
  count              = local.create_api_gateway ? 1 : 0
  api_id             = aws_apigatewayv2_api.coda[0].id
  integration_type   = "HTTP_PROXY"
  integration_method = "ANY"
  connection_type    = "VPC_LINK"
  connection_id      = aws_apigatewayv2_vpc_link.coda[0].id
  integration_uri    = module.runner_fargate[0].fargate_load_balancer_https_listener_arn

  tls_config {
    server_name_to_verify = local.runner_alb_domain
  }
}

################
# Routes — per-service for non-server, $default catch-all for server
#
# API Gateway v2 can't mix literal + parameter in one path segment, so
# per-package wildcards (e.g. /coda.core.v1.{proxy+}) are invalid.
#
# Strategy:
#   - Explicit routes for non-server services (platform, search, runner)
#   - $default catch-all for server (core, dashboards, tools)
#   - Health routes with no auth
#
# Adding new RPCs to any service requires zero gateway changes.
# Adding a new ConnectRPC service to a non-server app requires one new route.
################

# $default → Server (catches all core, dashboards, tools traffic)
resource "aws_apigatewayv2_route" "default" {
  count              = local.create_api_gateway ? 1 : 0
  api_id             = aws_apigatewayv2_api.coda[0].id
  route_key          = "$default"
  target             = "integrations/${aws_apigatewayv2_integration.server[0].id}"
  authorization_type = "JWT"
  authorizer_id      = aws_apigatewayv2_authorizer.auth0[0].id
}

# Admin → Platform
resource "aws_apigatewayv2_route" "admin_access" {
  count              = local.create_api_gateway ? 1 : 0
  api_id             = aws_apigatewayv2_api.coda[0].id
  route_key          = "POST /coda.admin.v1.AccessService/{proxy+}"
  target             = "integrations/${aws_apigatewayv2_integration.platform[0].id}"
  authorization_type = "JWT"
  authorizer_id      = aws_apigatewayv2_authorizer.auth0[0].id
}

# Search → Search
resource "aws_apigatewayv2_route" "search_search" {
  count              = local.create_api_gateway ? 1 : 0
  api_id             = aws_apigatewayv2_api.coda[0].id
  route_key          = "POST /coda.search.v1.SearchService/{proxy+}"
  target             = "integrations/${aws_apigatewayv2_integration.search[0].id}"
  authorization_type = "JWT"
  authorizer_id      = aws_apigatewayv2_authorizer.auth0[0].id
}

resource "aws_apigatewayv2_route" "search_admin" {
  count              = local.create_api_gateway ? 1 : 0
  api_id             = aws_apigatewayv2_api.coda[0].id
  route_key          = "POST /coda.search.v1.AdminService/{proxy+}"
  target             = "integrations/${aws_apigatewayv2_integration.search[0].id}"
  authorization_type = "JWT"
  authorizer_id      = aws_apigatewayv2_authorizer.auth0[0].id
}

# Datasources, Runner → Runner
resource "aws_apigatewayv2_route" "datasources" {
  count              = local.create_api_gateway ? 1 : 0
  api_id             = aws_apigatewayv2_api.coda[0].id
  route_key          = "POST /coda.datasources.v1.DatasourceService/{proxy+}"
  target             = "integrations/${aws_apigatewayv2_integration.runner[0].id}"
  authorization_type = "JWT"
  authorizer_id      = aws_apigatewayv2_authorizer.auth0[0].id
}

resource "aws_apigatewayv2_route" "runner" {
  count              = local.create_api_gateway ? 1 : 0
  api_id             = aws_apigatewayv2_api.coda[0].id
  route_key          = "POST /coda.runner.v1.RunnerService/{proxy+}"
  target             = "integrations/${aws_apigatewayv2_integration.runner[0].id}"
  authorization_type = "JWT"
  authorizer_id      = aws_apigatewayv2_authorizer.auth0[0].id
}

# Health — no auth required for external monitoring (Datadog, uptime checks, smoke tests)
resource "aws_apigatewayv2_route" "health" {
  # checkov:skip=CKV_AWS_309:Health check routes are intentionally unauthenticated for external monitoring
  count              = local.create_api_gateway ? 1 : 0
  api_id             = aws_apigatewayv2_api.coda[0].id
  route_key          = "GET /health"
  target             = "integrations/${aws_apigatewayv2_integration.server[0].id}"
  authorization_type = "NONE"
}

resource "aws_apigatewayv2_route" "health_ready" {
  # checkov:skip=CKV_AWS_309:Health check routes are intentionally unauthenticated for external monitoring
  count              = local.create_api_gateway ? 1 : 0
  api_id             = aws_apigatewayv2_api.coda[0].id
  route_key          = "GET /health/ready"
  target             = "integrations/${aws_apigatewayv2_integration.server[0].id}"
  authorization_type = "NONE"
}

################
# Custom Domain
#
# qa-coda-api.theorchard.io is a single-level subdomain, so the existing
# *.theorchard.io wildcard cert covers it — no dedicated ACM cert needed.
################

resource "aws_apigatewayv2_domain_name" "coda" {
  count       = local.create_api_gateway ? 1 : 0
  domain_name = var.api_gateway_domain

  domain_name_configuration {
    certificate_arn = data.aws_acm_certificate.theorchard_io.arn
    endpoint_type   = "REGIONAL"
    security_policy = "TLS_1_2"
  }
}

resource "aws_apigatewayv2_api_mapping" "coda" {
  count       = local.create_api_gateway ? 1 : 0
  api_id      = aws_apigatewayv2_api.coda[0].id
  domain_name = aws_apigatewayv2_domain_name.coda[0].domain_name
  stage       = aws_apigatewayv2_stage.default[0].id
}

resource "aws_route53_record" "api_gateway" {
  count    = local.create_api_gateway ? 1 : 0
  provider = aws.networking
  zone_id  = data.aws_route53_zone.theorchard_io[0].zone_id
  name     = var.api_gateway_domain
  type     = "A"

  alias {
    name                   = aws_apigatewayv2_domain_name.coda[0].domain_name_configuration[0].target_domain_name
    zone_id                = aws_apigatewayv2_domain_name.coda[0].domain_name_configuration[0].hosted_zone_id
    evaluate_target_health = false
  }
}
