data "aws_wafv2_web_acl" "service_waf" {
  name  = "${var.environment}-orcd-waf-block"
  scope = "REGIONAL"
}

data "aws_ec2_managed_prefix_list" "ingress_prefix_lists" {
  for_each = toset([
    "vpn-sme-internal-primary",
  ])
  name     = each.value
}

# Application Load Balancer.
resource "aws_lb" "load_balancer" {
  name               = local.load_balancer_name
  load_balancer_type = "application"
  internal           = true

  subnets = values(local.subnet_ids)
  security_groups = [
    aws_security_group.load_balancer_security_group.id,
  ]

  access_logs {
    enabled = true
    bucket  = "shared-orcd-lb-logs"
    prefix  = "${data.aws_caller_identity.current.account_id}/${local.load_balancer_name}"
  }

  tags = local.tags
}

resource "aws_lb_listener" "https_listener" {
  load_balancer_arn = aws_lb.load_balancer.arn

  port            = 443
  protocol        = "HTTPS"
  ssl_policy      = var.load_balancer_ssl_policy
  certificate_arn = data.aws_acm_certificate.acm_certificate.arn

  default_action {
    type = "authenticate-oidc"

    authenticate_oidc {
      issuer                 = "https://login.microsoftonline.com/f0aff3b7-91a5-4aae-af71-c63e1dda2049/v2.0"
      authorization_endpoint = "https://login.microsoftonline.com/f0aff3b7-91a5-4aae-af71-c63e1dda2049/oauth2/v2.0/authorize"
      token_endpoint         = "https://login.microsoftonline.com/f0aff3b7-91a5-4aae-af71-c63e1dda2049/oauth2/v2.0/token"
      user_info_endpoint     = "https://graph.microsoft.com/oidc/userinfo"

      client_id                  = var.load_balancer_azure_client_id
      client_secret              = var.load_balancer_azure_client_secret
      session_timeout            = var.load_balancer_session_timeout
      scope                      = "openid profile"
      on_unauthenticated_request = "authenticate"
    }
  }

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

  tags = local.tags
}

resource "aws_lb_listener_rule" "elevate_permissions_listener_rule" {
  listener_arn = aws_lb_listener.https_listener.arn
  priority     = 100

  action {
    type = "authenticate-oidc"

    authenticate_oidc {
      issuer                 = "https://login.microsoftonline.com/f0aff3b7-91a5-4aae-af71-c63e1dda2049/v2.0"
      authorization_endpoint = "https://login.microsoftonline.com/f0aff3b7-91a5-4aae-af71-c63e1dda2049/oauth2/v2.0/authorize"
      token_endpoint         = "https://login.microsoftonline.com/f0aff3b7-91a5-4aae-af71-c63e1dda2049/oauth2/v2.0/token"
      user_info_endpoint     = "https://graph.microsoft.com/oidc/userinfo"

      client_id                  = var.load_balancer_azure_client_id
      client_secret              = var.load_balancer_azure_client_secret
      session_timeout            = var.load_balancer_session_timeout
      scope                      = "openid profile"
      on_unauthenticated_request = "authenticate"
    }
  }

  action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.elevate_permissions_target_group.arn
  }

  condition {
    path_pattern {
      values = ["/elevate-permissions/*"]
    }
  }

  tags = local.tags

  lifecycle {
    ignore_changes = [
      action[0].authenticate_oidc[0].client_secret
    ]
  }
}

# ALB Target Group for elevate-permissions Lambda function.
resource "aws_lb_target_group" "elevate_permissions_target_group" {
  name        = local.load_balancer_elevate_permissions_target_group_name
  target_type = "lambda"
  tags        = local.tags
}

resource "aws_lb_target_group_attachment" "elevate_permissions_lambda_target_group_attachment" {
  target_group_arn = aws_lb_target_group.elevate_permissions_target_group.arn
  target_id        = module.elevate_permissions_lambda.lambda_arn

  depends_on = [
    aws_lambda_permission.load_balancer_elevate_permissions_lambda_permissions
  ]
}

resource "aws_lambda_permission" "load_balancer_elevate_permissions_lambda_permissions" {
  action        = "lambda:InvokeFunction"
  principal     = "elasticloadbalancing.amazonaws.com"
  source_arn    = aws_lb_target_group.elevate_permissions_target_group.arn
  function_name = module.elevate_permissions_lambda.lambda_arn
}

resource "aws_lb_listener_rule" "home_page_listener_rule" {
  listener_arn = aws_lb_listener.https_listener.arn
  priority     = 200

  action {
    type = "authenticate-oidc"

    authenticate_oidc {
      issuer                 = "https://login.microsoftonline.com/f0aff3b7-91a5-4aae-af71-c63e1dda2049/v2.0"
      authorization_endpoint = "https://login.microsoftonline.com/f0aff3b7-91a5-4aae-af71-c63e1dda2049/oauth2/v2.0/authorize"
      token_endpoint         = "https://login.microsoftonline.com/f0aff3b7-91a5-4aae-af71-c63e1dda2049/oauth2/v2.0/token"
      user_info_endpoint     = "https://graph.microsoft.com/oidc/userinfo"

      client_id                  = var.load_balancer_azure_client_id
      client_secret              = var.load_balancer_azure_client_secret
      session_timeout            = var.load_balancer_session_timeout
      scope                      = "openid profile"
      on_unauthenticated_request = "authenticate"
    }
  }

  action {
    type = "fixed-response"

    fixed_response {
      status_code  = 200
      content_type = "text/html"
      message_body = local.load_balancer_index_page_content
    }
  }

  condition {
    path_pattern {
      values = ["/home-page"]
    }
  }

  tags = local.tags

  lifecycle {
    ignore_changes = [
      action[0].authenticate_oidc[0].client_secret
    ]
  }
}

# ALB Target Group for elevate-permissions Lambda function.
resource "aws_lb_target_group" "home_page_target_group" {
  name        = local.load_balancer_home_page_target_group_name
  target_type = "lambda"
  tags        = local.tags
}

resource "aws_lb_target_group_attachment" "home_page_lambda_target_group_attachment" {
  target_group_arn = aws_lb_target_group.home_page_target_group.arn
  target_id        = module.home_page_lambda.lambda_arn

  depends_on = [
    aws_lambda_permission.load_balancer_home_page_lambda_permissions
  ]
}

resource "aws_lambda_permission" "load_balancer_home_page_lambda_permissions" {
  action        = "lambda:InvokeFunction"
  principal     = "elasticloadbalancing.amazonaws.com"
  source_arn    = aws_lb_target_group.home_page_target_group.arn
  function_name = module.home_page_lambda.lambda_arn
}

# ALB Route53 record.
resource "aws_route53_record" "load_balancer_route53_record" {
  zone_id = data.aws_route53_zone.route53_zone.id
  name    = local.load_balancer_route53_record_name
  type    = "A"

  alias {
    name                   = aws_lb.load_balancer.dns_name
    zone_id                = aws_lb.load_balancer.zone_id
    evaluate_target_health = true
  }
}

# ALB Security Group.
resource "aws_security_group" "load_balancer_security_group" {
  name        = local.load_balancer_security_group_name
  description = "Security Group for ${local.load_balancer_name} Application Load Balancer."
  vpc_id      = data.aws_vpc.vpc.id

  tags = merge(local.tags, {
    "Name"           = local.load_balancer_security_group_name,
    "eiso-exception" = "aws.08.30",
  })
}

resource "aws_security_group_rule" "load_balancer_security_group_ingress_https_rule" {
  type              = "ingress"
  protocol          = "tcp"
  cidr_blocks       = var.trusted_cidr_blocks
  from_port         = 443
  to_port           = 443
  security_group_id = aws_security_group.load_balancer_security_group.id
  prefix_list_ids   = [for prefix_list in data.aws_ec2_managed_prefix_list.ingress_prefix_lists : prefix_list.id]
}

resource "aws_security_group_rule" "load_balancer_security_group_egress_rule" {
  type              = "egress"
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]
  from_port         = 0
  to_port           = 0
  security_group_id = aws_security_group.load_balancer_security_group.id
}

# Attach the loadbalancer to the WAF
resource "aws_wafv2_web_acl_association" "lb_waf_association" {
  resource_arn = aws_lb.load_balancer.arn
  web_acl_arn  = data.aws_wafv2_web_acl.service_waf.arn
}
