# Load balancer ===============================================================

resource "aws_lb" "this" {
  name                       = local.lb_name
  load_balancer_type         = var.load_balancer_type
  internal                   = var.internal
  subnets                    = var.subnet_ids
  security_groups            = length(var.security_group_ids) > 0 ? var.security_group_ids : null
  enable_deletion_protection = var.deletion_protection
  idle_timeout               = local.is_alb ? var.idle_timeout : null

  enable_cross_zone_load_balancing = local.is_nlb ? var.cross_zone_load_balancing : null

  dynamic "access_logs" {
    for_each = var.access_logs_bucket != null ? [true] : []
    content {
      bucket  = var.access_logs_bucket
      prefix  = var.access_logs_prefix
      enabled = true
    }
  }

  tags = merge(var.tags, local.module_tags, {
    Name          = local.lb_name
    resource-type = "load-balancer"
  })

  lifecycle {
    postcondition {
      condition     = self.arn != ""
      error_message = "Load balancer ${self.name} did not receive an ARN after apply."
    }

    postcondition {
      condition     = self.dns_name != ""
      error_message = "Load balancer ${self.name} has no DNS name — check subnet and az coverage."
    }
  }
}

# WAF association --------------------------------------------------------------
# WAFv2 web ACLs only support ALB, API Gateway, and AppSync as targets.

resource "aws_wafv2_web_acl_association" "this" {
  for_each = var.waf_web_acl_arn != null && local.is_alb ? { waf = true } : {}

  resource_arn = aws_lb.this.arn
  web_acl_arn  = var.waf_web_acl_arn
}

# Target groups ================================================================

resource "aws_lb_target_group" "this" {
  for_each = var.target_groups

  name                 = local.target_group_names[each.key]
  port                 = each.value.port
  protocol             = each.value.protocol
  target_type          = each.value.target_type
  vpc_id               = each.value.vpc_id
  deregistration_delay = each.value.deregistration_delay

  health_check {
    enabled             = each.value.health_check.enabled
    path                = each.value.health_check.protocol == "TCP" ? null : each.value.health_check.path
    protocol            = each.value.health_check.protocol
    matcher             = each.value.health_check.protocol == "TCP" ? null : each.value.health_check.matcher
    interval            = each.value.health_check.interval
    timeout             = each.value.health_check.protocol == "TCP" ? null : each.value.health_check.timeout
    healthy_threshold   = each.value.health_check.healthy_threshold
    unhealthy_threshold = each.value.health_check.unhealthy_threshold
  }

  dynamic "stickiness" {
    for_each = each.value.stickiness != null ? [each.value.stickiness] : []
    content {
      enabled         = stickiness.value.enabled
      cookie_duration = stickiness.value.type == "source_ip" ? null : stickiness.value.cookie_duration
      type            = stickiness.value.type
    }
  }

  tags = merge(var.tags, each.value.tags, local.module_tags, {
    Name          = local.target_group_names[each.key]
    resource-type = "load-balancer-target-group"
  })

  lifecycle {
    postcondition {
      condition     = self.arn != ""
      error_message = "Target group ${self.name} did not receive an ARN after apply."
    }
  }
}

# Target group attachments ========================================================

resource "aws_lb_target_group_attachment" "this" {
  for_each = local.target_attachments

  target_group_arn = aws_lb_target_group.this[each.value.target_group_key].arn
  target_id        = each.value.target_id
  port             = each.value.port
}

# Listeners ======================================================================

resource "aws_lb_listener" "this" {
  for_each = var.listeners

  load_balancer_arn = aws_lb.this.arn
  port              = each.value.port
  protocol          = each.value.protocol
  certificate_arn   = each.value.certificate_arn
  ssl_policy        = each.value.certificate_arn != null ? each.value.ssl_policy : null

  dynamic "default_action" {
    for_each = each.value.redirect != null ? [] : [true]
    content {
      type             = each.value.default_action_type
      target_group_arn = each.value.target_group_key != null ? aws_lb_target_group.this[each.value.target_group_key].arn : null
    }
  }

  dynamic "default_action" {
    for_each = each.value.redirect != null ? [each.value.redirect] : []
    content {
      type = "redirect"
      redirect {
        port        = default_action.value.port
        protocol    = default_action.value.protocol
        status_code = default_action.value.status_code
      }
    }
  }

  tags = merge(var.tags, local.module_tags, {
    Name          = local.listener_keys[each.key]
    resource-type = "load-balancer-listener"
  })

  lifecycle {
    postcondition {
      condition     = self.arn != ""
      error_message = "Listener on port ${self.port} did not receive an ARN after apply."
    }
  }
}
