# ── CloudWatch Log Group ────────────────────────────────────

resource "aws_cloudwatch_log_group" "backend" {
  name              = "/ecs/${local.name_prefix}-backend"
  retention_in_days = 14
}

# ── ECS Cluster ────────────────────────────────────────────

resource "aws_ecs_cluster" "main" {
  name = "${local.name_prefix}-cluster"

  setting {
    name  = "containerInsights"
    value = "enabled"
  }
}

# ── Task Definition ────────────────────────────────────────

resource "aws_ecs_task_definition" "backend" {
  family                   = "${local.name_prefix}-backend"
  requires_compatibilities = ["FARGATE"]
  network_mode             = "awsvpc"
  cpu                      = var.ecs_cpu
  memory                   = var.ecs_memory
  execution_role_arn       = aws_iam_role.ecs_execution.arn
  task_role_arn            = aws_iam_role.ecs_task.arn

  container_definitions = jsonencode([{
    name      = "backend"
    image     = "${aws_ecr_repository.backend.repository_url}:${var.image_tag}"
    essential = true

    portMappings = [{
      containerPort = 3001
      protocol      = "tcp"
    }]

    environment = concat([
      { name = "PORT", value = "3001" },
      { name = "NEXUS_ENV", value = "production" },
      { name = "NODE_ENV", value = "production" },
      { name = "LOG_FORMAT", value = "json" },
      { name = "TIDB_TLS", value = "true" },
      { name = "CORS_ALLOWED_ORIGINS", value = local.frontend_base_url },
      { name = "FRONTEND_URL", value = local.frontend_base_url },
      { name = "API_BASE_URL", value = local.frontend_base_url },
      { name = "DEV_SESSION_BRANCH_DB", value = var.dev_session_branch_db },
      { name = "FILE_STORAGE_PROVIDER", value = lower(trimspace(var.file_storage_provider)) },
      # Tolerate legacy autonumber rows that pre-date the v2 cutover (PR #1140).
      # Required for prod where org-shape drift (e.g. user-created RTs without
      # codes, manually-renamed fields) would otherwise fail the V6/V8 invariant
      # checks in cmd/setup apply AND cmd/server readiness (three-layer gate
      # from PR #1179 / #1180 / #1182). See issue #1631 B-1. Defaults to "true"
      # via variable; non-prod can override to "false" in its tfvars to exercise
      # strict mode against a clone of prod data.
      { name = "AUTONUMBER_SEED_TOLERATE_LEGACY", value = var.autonumber_seed_tolerate_legacy },
      ],
      trimspace(var.tenant_overlay_dir) != "" ? [
        # Runtime tenant metadata selection. Generic deployments omit this;
        # tenant deployments pass an explicit overlay directory.
        { name = "NEXUSCRM_TENANT_OVERLAY_DIR", value = trimspace(var.tenant_overlay_dir) },
      ] : [],
      var.apply_tenant_overlay == "1" ? [
        # Apply is a separate boot-time mutation switch. A tenant deployment
        # may keep NEXUSCRM_TENANT_OVERLAY_DIR set while setting this to "0"
        # after the DB has already been seeded.
        { name = "NEXUSCRM_APPLY_TENANT_OVERLAY", value = var.apply_tenant_overlay },
      ] : [],
      trimspace(var.file_storage_bucket) != "" ? [
        { name = "FILE_STORAGE_BUCKET", value = trimspace(var.file_storage_bucket) },
        { name = "FILE_STORAGE_REGION", value = local.file_storage_region },
      ] : [],
      trimspace(var.file_storage_prefix) != "" ? [
        { name = "FILE_STORAGE_PREFIX", value = trimspace(var.file_storage_prefix) },
      ] : [],
      # BOOTSTRAP_SCHEMA_MODE removed — server is read-only, setup runs via entrypoint.sh
      var.tidb_cloud_cluster_id != "" ? [
        { name = "TIDB_CLOUD_CLUSTER_ID", value = var.tidb_cloud_cluster_id },
      ] : [],
      # PR #1850 audit V2 + request capture opt-in. Omitted entirely when the
      # TF_VAR is empty so backend defaults (off) win. Flip on per-env via the
      # TF_VAR_AUDIT_V2_ENABLED / TF_VAR_NEXUSCRM_REQUEST_CAPTURE GitHub Env
      # secret, never in default tfvars (prod-safety invariant C17.3).
      var.audit_v2_enabled != "" ? [
        { name = "AUDIT_V2_ENABLED", value = var.audit_v2_enabled },
      ] : [],
      var.nexuscrm_request_capture != "" ? [
        { name = "NEXUSCRM_REQUEST_CAPTURE", value = var.nexuscrm_request_capture },
    ] : [])

    secrets = concat([
      { name = "TIDB_HOST", valueFrom = try(aws_ssm_parameter.tidb_branch_host[0].arn, aws_ssm_parameter.tidb_host.arn) },
      { name = "TIDB_PORT", valueFrom = aws_ssm_parameter.tidb_port.arn },
      { name = "TIDB_USER", valueFrom = try(aws_ssm_parameter.tidb_branch_user[0].arn, aws_ssm_parameter.tidb_user.arn) },
      { name = "TIDB_PASSWORD", valueFrom = try(aws_ssm_parameter.tidb_branch_password[0].arn, aws_ssm_parameter.tidb_password.arn) },
      { name = "TIDB_DATABASE", valueFrom = aws_ssm_parameter.tidb_database.arn },
      { name = "DEV_SESSION_ENCRYPTION_KEY", valueFrom = aws_ssm_parameter.tidb_password.arn },
      { name = "JWT_SECRET", valueFrom = aws_ssm_parameter.jwt_secret.arn },
      { name = "NEXUS_ADMIN_PASSWORD", valueFrom = aws_ssm_parameter.admin_password.arn },
      ], var.tidb_cloud_public_key != "" ? [
      { name = "TIDB_CLOUD_PUBLIC_KEY", valueFrom = aws_ssm_parameter.tidb_cloud_public_key[0].arn },
      { name = "TIDB_CLOUD_PRIVATE_KEY", valueFrom = aws_ssm_parameter.tidb_cloud_private_key[0].arn },
      ] : [], var.google_client_id != "" ? [
      { name = "GOOGLE_CLIENT_ID", valueFrom = aws_ssm_parameter.google_client_id[0].arn },
      { name = "GOOGLE_CLIENT_SECRET", valueFrom = aws_ssm_parameter.google_client_secret[0].arn },
      ] : [], var.smtp_ses_domain != "" ? [
      { name = "SMTP_HOST", valueFrom = aws_ssm_parameter.smtp_host[0].arn },
      { name = "SMTP_PORT", valueFrom = aws_ssm_parameter.smtp_port[0].arn },
      { name = "SMTP_USER", valueFrom = aws_ssm_parameter.smtp_user[0].arn },
      { name = "SMTP_PASSWORD", valueFrom = aws_ssm_parameter.smtp_password[0].arn },
      { name = "SMTP_FROM", valueFrom = aws_ssm_parameter.smtp_from[0].arn },
      { name = "SMTP_TLS", valueFrom = aws_ssm_parameter.smtp_tls[0].arn },
    ] : [])

    logConfiguration = {
      logDriver = "awslogs"
      options = {
        "awslogs-group"         = aws_cloudwatch_log_group.backend.name
        "awslogs-region"        = var.aws_region
        "awslogs-stream-prefix" = "ecs"
      }
    }
  }])
}

# ── ALB ────────────────────────────────────────────────────

resource "aws_lb" "main" {
  name               = "${local.name_prefix}-alb"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.alb.id]
  subnets            = aws_subnet.public[*].id
  idle_timeout       = 120

  tags = { Name = "${local.name_prefix}-alb" }
}

resource "aws_lb_target_group" "backend" {
  name        = "${local.name_prefix}-backend-tg"
  port        = 3001
  protocol    = "HTTP"
  vpc_id      = aws_vpc.main.id
  target_type = "ip"

  health_check {
    path                = "/health"
    interval            = 30
    timeout             = 5
    healthy_threshold   = 2
    unhealthy_threshold = 3
    matcher             = "200"
  }

  lifecycle { create_before_destroy = true }
}

resource "aws_lb_listener" "http" {
  load_balancer_arn = aws_lb.main.arn
  port              = 80
  protocol          = "HTTP"

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.backend.arn
  }
}

# ── ECS Service ────────────────────────────────────────────

resource "aws_ecs_service" "backend" {
  name                 = "${local.name_prefix}-backend"
  cluster              = aws_ecs_cluster.main.id
  task_definition      = aws_ecs_task_definition.backend.arn
  desired_count        = var.ecs_desired_count
  launch_type          = "FARGATE"
  force_new_deployment = true

  network_configuration {
    subnets          = aws_subnet.private[*].id
    security_groups  = [aws_security_group.ecs.id]
    assign_public_ip = false
  }

  load_balancer {
    target_group_arn = aws_lb_target_group.backend.arn
    container_name   = "backend"
    container_port   = 3001
  }

  health_check_grace_period_seconds  = 1800 # 15m cold start observed (8 indices + lock wait + backfill + autonumber)
  deployment_minimum_healthy_percent = 100
  deployment_maximum_percent         = 200

  deployment_circuit_breaker {
    enable   = true
    rollback = true
  }

  depends_on = [aws_lb_listener.http]
}
