/**
 * Copyright 2025 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

# tfdoc:file:description Regular service perimeter resources.

locals {
  egress_policies = merge(
    local.data.egress_policies,
    { for k, v in var.egress_policies : "$egress_policies:${k}" => v }
  )
  ingress_policies = merge(
    local.data.ingress_policies,
    { for k, v in var.ingress_policies : "$ingress_policies:${k}" => v }
  )
  perimeters = merge(local.data.perimeters, var.perimeters)
  _undefined_egress_policies = {
    for k, v in local.perimeters : k => setsubtract(concat(
      try(v.spec.egress_policies, []),
      try(v.status.egress_policies, [])),
      keys(local.egress_policies)
    )
  }
  _undefined_ingress_policies = {
    for k, v in local.perimeters :
    k => setsubtract(concat(
      try(v.spec.ingress_policies, []),
      try(v.status.ingress_policies, [])),
      keys(local.ingress_policies)
    )
  }
}

resource "google_access_context_manager_service_perimeter" "regular" {
  for_each = {
    for k, v in local.perimeters : k => v if !v.ignore_resource_changes
  }
  parent                    = "accessPolicies/${local.access_policy}"
  name                      = "accessPolicies/${local.access_policy}/servicePerimeters/${each.key}"
  description               = each.value.description
  title                     = coalesce(each.value.title, each.key)
  perimeter_type            = "PERIMETER_TYPE_REGULAR"
  use_explicit_dry_run_spec = each.value.use_explicit_dry_run_spec
  dynamic "spec" {
    for_each = each.value.spec == null ? [] : [each.value.spec]
    iterator = spec
    content {
      access_levels = (
        spec.value.access_levels == null ? null : [
          for k in spec.value.access_levels :
          lookup(local.ctx_access_levels, k, k)
        ]
      )
      resources = flatten([
        for r in spec.value.resources : try(
          local.ctx.resource_sets[r],
          [local.ctx.project_numbers[r]],
          [local.project_numbers[r]], [r]
        )
      ])
      restricted_services = flatten([
        for r in coalesce(spec.value.restricted_services, []) :
        lookup(local.ctx.service_sets, r, [r])
      ])

      dynamic "egress_policies" {
        for_each = [
          for k in coalesce(spec.value.egress_policies, []) :
          merge(local.egress_policies[k], { key = k })
        ]
        iterator = policy
        content {
          title = replace(coalesce(policy.value.title, policy.value.key), "$egress_policies:", "")
          dynamic "egress_from" {
            for_each = policy.value.from == null ? [] : [""]
            content {
              identity_type = policy.value.from.identity_type
              identities = policy.value.from.identities == null ? null : flatten([
                for i in coalesce(policy.value.from.identities, []) : (
                  startswith(i, "$identity_sets:")
                  ? lookup(local.ctx.identity_sets, i, [i])
                  : lookup(local.ctx.iam_principals_list, i, [i])
                )
              ])
              source_restriction = (
                length(policy.value.from.access_levels) > 0 || length(policy.value.from.resources) > 0
                ? "SOURCE_RESTRICTION_ENABLED"
                : "SOURCE_RESTRICTION_DISABLED"
              )
              dynamic "sources" {
                for_each = policy.value.from.access_levels
                iterator = access_level
                content {
                  access_level = lookup(
                    local.ctx_access_levels, access_level.value, access_level.value
                  )
                }
              }
              dynamic "sources" {
                for_each = flatten([
                  for r in policy.value.from.resources : try(
                    local.ctx.resource_sets[r],
                    [local.ctx.project_numbers[r]],
                    [local.project_numbers[r]], [r]
                  )
                ])
                iterator = resource
                content {
                  resource = resource.value
                }
              }
            }
          }
          dynamic "egress_to" {
            for_each = policy.value.to == null ? [] : [""]
            content {
              external_resources = policy.value.to.external_resources
              resources = flatten([
                for r in policy.value.to.resources : try(
                  local.ctx.resource_sets[r],
                  [local.ctx.project_numbers[r]],
                  [local.project_numbers[r]], [r]
                )
              ])
              roles = policy.value.to.roles
              dynamic "operations" {
                for_each = toset(policy.value.to.operations)
                iterator = o
                content {
                  service_name = o.value.service_name
                  dynamic "method_selectors" {
                    for_each = toset(coalesce(o.value.method_selectors, []))
                    content {
                      method = method_selectors.key
                    }
                  }
                  dynamic "method_selectors" {
                    for_each = toset(coalesce(o.value.permission_selectors, []))
                    content {
                      permission = method_selectors.key
                    }
                  }
                }
              }
            }
          }
        }
      }

      dynamic "ingress_policies" {
        for_each = [
          for k in coalesce(spec.value.ingress_policies, []) :
          merge(local.ingress_policies[k], { key = k })
        ]
        iterator = policy
        content {
          title = replace(coalesce(policy.value.title, policy.value.key), "$ingress_policies:", "")
          dynamic "ingress_from" {
            for_each = policy.value.from == null ? [] : [""]
            content {
              identity_type = policy.value.from.identity_type
              identities = policy.value.from.identities == null ? null : flatten([
                for i in coalesce(policy.value.from.identities, []) : (
                  startswith(i, "$identity_sets:")
                  ? lookup(local.ctx.identity_sets, i, [i])
                  : lookup(local.ctx.iam_principals_list, i, [i])
                )
              ])
              dynamic "sources" {
                for_each = toset(policy.value.from.access_levels)
                iterator = s
                content {
                  access_level = lookup(local.ctx_access_levels, s.value, s.value)
                }
              }
              dynamic "sources" {
                for_each = flatten([
                  for r in policy.value.from.resources : try(
                    local.ctx.resource_sets[r],
                    [local.ctx.project_numbers[r]],
                    [local.project_numbers[r]], [r]
                  )
                ])
                content {
                  resource = sources.value
                }
              }
            }
          }
          dynamic "ingress_to" {
            for_each = policy.value.to == null ? [] : [""]
            content {
              resources = flatten([
                for r in policy.value.to.resources : try(
                  local.ctx.resource_sets[r],
                  [local.ctx.project_numbers[r]],
                  [local.project_numbers[r]], [r]
                )
              ])
              roles = policy.value.to.roles
              dynamic "operations" {
                for_each = toset(policy.value.to.operations)
                iterator = o
                content {
                  service_name = o.value.service_name
                  dynamic "method_selectors" {
                    for_each = toset(coalesce(o.value.method_selectors, []))
                    content {
                      method = method_selectors.value
                    }
                  }
                  dynamic "method_selectors" {
                    for_each = toset(coalesce(o.value.permission_selectors, []))
                    content {
                      permission = method_selectors.value
                    }
                  }
                }
              }
            }
          }
        }
      }

      dynamic "vpc_accessible_services" {
        for_each = spec.value.vpc_accessible_services == null ? [] : [""]
        content {
          allowed_services = flatten([
            for r in spec.value.vpc_accessible_services.allowed_services :
            lookup(local.ctx.service_sets, r, [r])
          ])
          enable_restriction = spec.value.vpc_accessible_services.enable_restriction
        }
      }

    }
  }
  dynamic "status" {
    for_each = each.value.status == null ? [] : [each.value.status]
    iterator = status
    content {
      access_levels = (
        status.value.access_levels == null ? null : [
          for k in status.value.access_levels :
          lookup(local.ctx_access_levels, k, k)
        ]
      )
      resources = flatten([
        for r in status.value.resources : try(
          local.ctx.resource_sets[r],
          [local.ctx.project_numbers[r]],
          [local.project_numbers[r]], [r]
        )
      ])
      restricted_services = flatten([
        for r in coalesce(status.value.restricted_services, []) :
        lookup(local.ctx.service_sets, r, [r])
      ])

      dynamic "egress_policies" {
        for_each = [
          for k in coalesce(status.value.egress_policies, []) :
          merge(local.egress_policies[k], { key = k })
        ]
        iterator = policy
        content {
          title = replace(coalesce(policy.value.title, policy.value.key), "$egress_policies:", "")
          dynamic "egress_from" {
            for_each = policy.value.from == null ? [] : [""]
            content {
              identity_type = policy.value.from.identity_type
              identities = policy.value.from.identities == null ? null : flatten([
                for i in coalesce(policy.value.from.identities, []) : (
                  startswith(i, "$identity_sets:")
                  ? lookup(local.ctx.identity_sets, i, [i])
                  : lookup(local.ctx.iam_principals_list, i, [i])
                )
              ])
              source_restriction = (
                length(policy.value.from.access_levels) > 0 || length(policy.value.from.resources) > 0
                ? "SOURCE_RESTRICTION_ENABLED"
                : "SOURCE_RESTRICTION_DISABLED"
              )
              dynamic "sources" {
                for_each = policy.value.from.access_levels
                iterator = access_level
                content {
                  access_level = lookup(
                    local.ctx_access_levels, access_level.value, access_level.value
                  )
                }
              }
              dynamic "sources" {
                for_each = flatten([
                  for r in policy.value.from.resources : try(
                    local.ctx.resource_sets[r],
                    [local.ctx.project_numbers[r]],
                    [local.project_numbers[r]], [r]
                  )
                ])
                iterator = resource
                content {
                  resource = resource.value
                }
              }
            }
          }
          dynamic "egress_to" {
            for_each = policy.value.to == null ? [] : [""]
            content {
              external_resources = policy.value.to.external_resources
              resources = flatten([
                for r in policy.value.to.resources : try(
                  local.ctx.resource_sets[r],
                  [local.ctx.project_numbers[r]],
                  [local.project_numbers[r]], [r]
                )
              ])
              roles = policy.value.to.roles
              dynamic "operations" {
                for_each = toset(policy.value.to.operations)
                iterator = o
                content {
                  service_name = o.value.service_name
                  dynamic "method_selectors" {
                    for_each = toset(coalesce(o.value.method_selectors, []))
                    content {
                      method = method_selectors.key
                    }
                  }
                  dynamic "method_selectors" {
                    for_each = toset(coalesce(o.value.permission_selectors, []))
                    content {
                      permission = method_selectors.key
                    }
                  }
                }
              }
            }
          }
        }
      }

      dynamic "ingress_policies" {
        for_each = [
          for k in coalesce(status.value.ingress_policies, []) :
          merge(local.ingress_policies[k], { key = k })
        ]
        iterator = policy
        content {
          title = replace(coalesce(policy.value.title, policy.value.key), "$ingress_policies:", "")
          dynamic "ingress_from" {
            for_each = policy.value.from == null ? [] : [""]
            content {
              identity_type = policy.value.from.identity_type
              identities = policy.value.from.identities == null ? null : flatten([
                for i in coalesce(policy.value.from.identities, []) : (
                  startswith(i, "$identity_sets:")
                  ? lookup(local.ctx.identity_sets, i, [i])
                  : lookup(local.ctx.iam_principals_list, i, [i])
                )
              ])
              dynamic "sources" {
                for_each = toset(policy.value.from.access_levels)
                iterator = s
                content {
                  access_level = lookup(local.ctx_access_levels, s.value, s.value)
                }
              }
              dynamic "sources" {
                for_each = flatten([
                  for r in policy.value.from.resources : try(
                    local.ctx.resource_sets[r],
                    [local.ctx.project_numbers[r]],
                    [local.project_numbers[r]], [r]
                  )
                ])
                content {
                  resource = sources.value
                }
              }
            }
          }
          dynamic "ingress_to" {
            for_each = policy.value.to == null ? [] : [""]
            content {
              resources = flatten([
                for r in policy.value.to.resources : try(
                  local.ctx.resource_sets[r],
                  [local.ctx.project_numbers[r]],
                  [local.project_numbers[r]], [r]
                )
              ])
              roles = policy.value.to.roles
              dynamic "operations" {
                for_each = toset(policy.value.to.operations)
                iterator = o
                content {
                  service_name = o.value.service_name
                  dynamic "method_selectors" {
                    for_each = toset(coalesce(o.value.method_selectors, []))
                    content {
                      method = method_selectors.value
                    }
                  }
                  dynamic "method_selectors" {
                    for_each = toset(coalesce(o.value.permission_selectors, []))
                    content {
                      permission = method_selectors.value
                    }
                  }
                }
              }
            }
          }
        }
      }

      dynamic "vpc_accessible_services" {
        for_each = status.value.vpc_accessible_services == null ? [] : [""]
        content {
          allowed_services = flatten([
            for r in status.value.vpc_accessible_services.allowed_services :
            lookup(local.ctx.service_sets, r, [r])
          ])
          enable_restriction = status.value.vpc_accessible_services.enable_restriction
        }
      }

    }
  }
  lifecycle {
    precondition {
      condition     = length(local._undefined_ingress_policies[each.key]) == 0
      error_message = "Undefined ingress policies: ${join(", ", local._undefined_ingress_policies[each.key])}"
    }
    precondition {
      condition     = length(local._undefined_egress_policies[each.key]) == 0
      error_message = "Undefined egress policies: ${join(", ", local._undefined_egress_policies[each.key])}"
    }
  }
  depends_on = [
    google_access_context_manager_access_policy.default,
    google_access_context_manager_access_level.basic
  ]
}
