locals {
  name_prefix        = lower("${var.env_prefix}-${var.project_group}")
  metric_name_prefix = "${var.env_prefix}${var.project_group}"
  account_alias      = data.aws_iam_account_alias.current.account_alias

  ipv4_set_name = var.name == "" ? "${local.account_alias}_ipv4_set" : "${local.name_prefix}-${var.name}_ipv4_set"
  ipv6_set_name = var.name == "" ? "${local.account_alias}_ipv6_set" : "${local.name_prefix}-${var.name}_ipv6_set"
  web_acl_name  = var.name == "" ? "${local.account_alias}_web_acl" : "${local.name_prefix}-${var.name}_web_acl"

  allow = var.action == "allow" ? ["Allow"] : []
  block = var.action == "block" ? ["Block"] : []
}

data "aws_iam_account_alias" "current" {}

resource "aws_wafv2_ip_set" "this_ipv4" {
  name               = local.ipv4_set_name
  scope              = var.scope
  ip_address_version = "IPV4"
  addresses          = var.ipv4_addresses

  tags = merge(
    var.common_tags,
    {
      "Name" = local.ipv4_set_name
    }
  )

  lifecycle {
    create_before_destroy = true
  }

  provider = aws
}

resource "aws_wafv2_ip_set" "this_ipv6" {
  name               = local.ipv6_set_name
  scope              = var.scope
  ip_address_version = "IPV6"
  addresses          = var.ipv6_addresses

  tags = merge(
    var.common_tags,
    {
      "Name" = local.ipv6_set_name
    }
  )

  lifecycle {
    create_before_destroy = true
  }

  provider = aws
}

resource "aws_wafv2_web_acl" "this" {
  name  = local.web_acl_name
  scope = var.scope

  // default action is opposite to action in defined in a rule
  default_action {
    dynamic "allow" {
      for_each = local.block
      content {}
    }
    dynamic "block" {
      for_each = local.allow
      content {}
    }
  }

  rule {
    name     = "${local.name_prefix}-rule-ipv4"
    priority = 0

    action {
      dynamic "allow" {
        for_each = local.allow
        content {}
      }

      dynamic "block" {
        for_each = local.block
        content {}
      }
    }

    statement {
      ip_set_reference_statement {
        arn = aws_wafv2_ip_set.this_ipv4.arn
      }
    }

    visibility_config {
      cloudwatch_metrics_enabled = false
      metric_name                = "${local.metric_name_prefix}Rule"
      sampled_requests_enabled   = false
    }
  }

  rule {
    name     = "${local.name_prefix}-rule-ipv6"
    priority = 1

    action {
      dynamic "allow" {
        for_each = local.allow
        content {}
      }

      dynamic "block" {
        for_each = local.block
        content {}
      }
    }

    statement {
      ip_set_reference_statement {
        arn = aws_wafv2_ip_set.this_ipv6.arn
      }
    }

    visibility_config {
      cloudwatch_metrics_enabled = false
      metric_name                = "${local.metric_name_prefix}Rule"
      sampled_requests_enabled   = false
    }
  }

  rule {
    name     = "AWS-AWSManagedRulesKnownBadInputsRuleSet"
    priority = 2

    override_action {
      none {}
    }

    statement {
      managed_rule_group_statement {
        name        = "AWSManagedRulesKnownBadInputsRuleSet"
        vendor_name = "AWS"
      }
    }

    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "AWS-AWSManagedRulesKnownBadInputsRuleSet"
      sampled_requests_enabled   = true
    }
  }

  visibility_config {
    cloudwatch_metrics_enabled = false
    metric_name                = "${local.metric_name_prefix}WebAcl"
    sampled_requests_enabled   = false
  }

  tags = merge(
    var.common_tags,
    {
      "Name" = local.web_acl_name
    }
  )

  provider = aws
  depends_on = [
    aws_wafv2_ip_set.this_ipv4,
    aws_wafv2_ip_set.this_ipv6,
  ]
  lifecycle {
    ignore_changes = [tags]
  }
}

resource "aws_wafv2_web_acl_logging_configuration" "this" {
  log_destination_configs = [var.shared_waf_logs_bucket_arn]
  resource_arn            = aws_wafv2_web_acl.this.arn

  provider = aws
}
