/**
 * 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.
 */

locals {
  _ctx_p = "$"
  ctx = {
    for k, v in var.context : k => {
      for kk, vv in v : "${local._ctx_p}${k}:${kk}" => vv
    } if !endswith(k, "_vars")
  }
  bucket = (
    var.bucket_config == null
    ? var.bucket_name
    : (
      length(google_storage_bucket.bucket) > 0
      ? google_storage_bucket.bucket[0].name
      : null
    )
  )
  location   = lookup(local.ctx.locations, var.region, var.region)
  prefix     = var.prefix == null ? "" : "${var.prefix}-"
  project_id = lookup(local.ctx.project_ids, var.project_id, var.project_id)
  trigger_sa_create = (
    try(var.trigger_config.service_account_create, false) == true
  )
  trigger_sa_email = try(
    google_service_account.trigger_service_account[0].email,
    var.trigger_config.service_account_email,
    null
  )
  vpc_connector = var.vpc_connector_create != null ? google_vpc_access_connector.connector[0].id : var.vpc_connector.name
}

resource "google_cloudfunctions2_function" "function" {
  provider     = google-beta
  project      = local.project_id
  location     = local.location
  name         = "${local.prefix}${var.name}"
  description  = var.description
  kms_key_name = var.kms_key == null ? null : lookup(local.ctx.kms_keys, var.kms_key, var.kms_key)
  build_config {
    service_account       = var.build_service_account
    worker_pool           = var.build_worker_pool
    runtime               = var.function_config.runtime
    entry_point           = var.function_config.entry_point
    environment_variables = var.build_environment_variables
    docker_repository     = var.docker_repository_id

    dynamic "automatic_update_policy" {
      for_each = try(var.function_config.automatic_update_policy, null) == true ? [""] : []
      content {}
    }

    dynamic "on_deploy_update_policy" {
      for_each = try(var.function_config.on_deploy_update_policy, null) == true ? [""] : []
      content {}
    }

    source {
      storage_source {
        bucket = local.bucket
        object = (
          local.bundle_type == "gcs"
          ? replace(var.bundle_config.path, "/^gs:\\/\\/[^\\/]+\\//", "")
          : google_storage_bucket_object.bundle[0].name
        )
      }
    }
  }
  dynamic "event_trigger" {
    for_each = var.trigger_config == null ? [] : [""]
    content {
      event_type   = var.trigger_config.event_type
      pubsub_topic = var.trigger_config.pubsub_topic
      trigger_region = (
        var.trigger_config.region == null
        ? var.region
        : var.trigger_config.region
      )
      dynamic "event_filters" {
        for_each = var.trigger_config.event_filters
        iterator = event_filter
        content {
          attribute = event_filter.value.attribute
          value     = event_filter.value.value
          operator  = event_filter.value.operator
        }
      }
      service_account_email = local.trigger_sa_email
      retry_policy          = var.trigger_config.retry_policy
    }
  }
  service_config {
    all_traffic_on_latest_revision   = true
    available_cpu                    = var.function_config.cpu
    available_memory                 = "${var.function_config.memory_mb}M"
    binary_authorization_policy      = var.function_config.binary_authorization_policy
    environment_variables            = var.environment_variables
    ingress_settings                 = var.ingress_settings
    max_instance_count               = var.function_config.instance_count
    max_instance_request_concurrency = var.function_config.max_instance_request_concurrency
    min_instance_count               = var.function_config.min_instance_count
    service_account_email            = local.service_account_email
    timeout_seconds                  = var.function_config.timeout_seconds
    vpc_connector                    = local.vpc_connector
    vpc_connector_egress_settings    = var.vpc_connector.egress_settings
    direct_vpc_egress                = try(var.direct_vpc_egress.mode, null)

    dynamic "direct_vpc_network_interface" {
      for_each = var.direct_vpc_egress == null ? [] : [""]
      content {
        network = lookup(
          local.ctx.networks, var.direct_vpc_egress.network,
          var.direct_vpc_egress.network
        )
        subnetwork = lookup(
          local.ctx.subnets, var.direct_vpc_egress.subnetwork,
          var.direct_vpc_egress.subnetwork
        )
        tags = var.direct_vpc_egress.tags
      }
    }

    dynamic "secret_environment_variables" {
      for_each = { for k, v in var.secrets : k => v if !v.is_volume }
      iterator = secret
      content {
        key        = secret.key
        project_id = secret.value.project_id
        secret     = secret.value.secret
        version    = try(secret.value.versions[0], "latest")
      }
    }

    dynamic "secret_volumes" {
      for_each = { for k, v in var.secrets : k => v if v.is_volume }
      iterator = secret
      content {
        mount_path = secret.key
        project_id = secret.value.project_id
        secret     = secret.value.secret
        dynamic "versions" {
          for_each = secret.value.versions
          iterator = version
          content {
            path    = split(":", version.value)[1]
            version = split(":", version.value)[0]
          }
        }
      }
    }
  }
  labels = var.labels
}

resource "google_cloudfunctions2_function_iam_binding" "binding" {
  for_each = {
    for k, v in var.iam : k => v if k != "roles/run.invoker"
  }
  project        = local.project_id
  location       = local.location
  cloud_function = google_cloudfunctions2_function.function.name
  role           = lookup(local.ctx.custom_roles, each.key, each.key)
  members        = [for member in each.value : lookup(local.ctx.iam_principals, member, member)]
  lifecycle {
    replace_triggered_by = [google_cloudfunctions2_function.function.id]
  }
}

locals {
  run_invoker_members = distinct(compact(concat(
    !local.trigger_sa_create
    ? []
    : ["serviceAccount:${local.trigger_sa_email}"],
    lookup(var.iam, "roles/run.invoker", []),
  )))
}

resource "google_cloud_run_service_iam_binding" "invoker" {
  # cloud run resources are needed for invoker role to the underlying service
  count = (
    lookup(var.iam, "roles/run.invoker", null) != null
  ) ? 1 : 0
  project  = local.project_id
  location = local.location
  service  = google_cloudfunctions2_function.function.name
  role     = "roles/run.invoker"
  members  = [for member in local.run_invoker_members : lookup(local.ctx.iam_principals, member, member)]
  lifecycle {
    replace_triggered_by = [google_cloudfunctions2_function.function.id]
  }
}

resource "google_cloud_run_service_iam_member" "invoker" {
  # if authoritative invoker role is not present and we create trigger sa
  # use additive binding to grant it the role
  count = (
    lookup(var.iam, "roles/run.invoker", null) == null &&
    local.trigger_sa_create
  ) ? 1 : 0
  project  = local.project_id
  location = local.location
  service  = google_cloudfunctions2_function.function.name
  role     = "roles/run.invoker"
  member   = "serviceAccount:${local.trigger_sa_email}"
  lifecycle {
    replace_triggered_by = [google_cloudfunctions2_function.function.id]
  }
}

resource "google_service_account" "trigger_service_account" {
  count        = local.trigger_sa_create ? 1 : 0
  project      = var.project_id
  account_id   = "tf-cf-trigger-${var.name}"
  display_name = "Terraform trigger for Cloud Function ${var.name}."
}
