/**
 * Copyright 2022 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 {
  _group_iam = {
    for r in local._group_iam_bindings : r => [
      for k, v in var.group_iam :
      "group:${k}" if try(index(v, r), null) != null
    ]
  }
  _group_iam_additive = {
    for r in local._group_iam_additive_bindings : r => [
      for k, v in var.group_iam_additive :
      "group:${k}" if try(index(v, r), null) != null
    ]
  }
  _group_iam_bindings          = distinct(flatten(values(var.group_iam)))
  _group_iam_additive_bindings = distinct(flatten(values(var.group_iam_additive)))

  _service_accounts_iam = {
    for r in local._service_accounts_iam_bindings : r => [
      for k, v in var.service_accounts :
      module.service-accounts[k].iam_email
      if try(index(v, r), null) != null
    ]
  }
  _service_accounts_iam_bindings = distinct(flatten(
    values(var.service_accounts)
  ))
  _service_accounts_iam_additive = {
    for r in local._service_accounts_iam_additive_bindings : r => [
      for k, v in var.service_accounts_additive :
      module.service-accounts[k].iam_email
      if try(index(v, r), null) != null
    ]
  }
  _service_accounts_iam_additive_bindings = distinct(flatten(
    values(var.service_accounts_additive)
  ))
  _services = concat([
    "billingbudgets.googleapis.com",
    "essentialcontacts.googleapis.com",
    "orgpolicy.googleapis.com",
    ],
    length(var.dns_zones) > 0 ? ["dns.googleapis.com"] : [],
    try(var.vpc.gke_setup, null) != null ? ["container.googleapis.com"] : [],
    var.vpc != null ? ["compute.googleapis.com"] : [],
  )
  _service_identities_roles = distinct(flatten(values(var.service_identities_iam)))
  _service_identities_iam = {
    for role in local._service_identities_roles : role => [
      for service, roles in var.service_identities_iam :
      "serviceAccount:${module.project.service_accounts.robots[service]}"
      if contains(roles, role)
    ]
  }
  _service_identities_roles_additive = distinct(flatten(values(var.service_identities_iam_additive)))
  _service_identities_iam_additive = {
    for role in local._service_identities_roles_additive : role => [
      for service, roles in var.service_identities_iam_additive :
      "serviceAccount:${module.project.service_accounts.robots[service]}"
      if contains(roles, role)
    ]
  }
  _vpc_subnet_bindings = (
    local.vpc.subnets_iam == null || local.vpc.host_project == null
    ? []
    : flatten([
      for subnet, members in local.vpc.subnets_iam : [
        for member in members : {
          region = split("/", subnet)[0]
          subnet = split("/", subnet)[1]
          member = member
        }
      ]
    ])
  )
  billing_account_id = coalesce(
    var.billing_account_id, try(var.defaults.billing_account_id, "")
  )
  billing_alert = (
    var.billing_alert == null
    ? try(var.defaults.billing_alert, null)
    : var.billing_alert
  )
  essential_contacts = concat(
    try(var.defaults.essential_contacts, []), var.essential_contacts
  )
  iam = {
    for role in distinct(concat(
      keys(var.iam),
      keys(local._group_iam),
      keys(local._service_accounts_iam),
      keys(local._service_identities_iam),
    )) :
    role => concat(
      try(var.iam[role], []),
      try(local._group_iam[role], []),
      try(local._service_accounts_iam[role], []),
      try(local._service_identities_iam[role], []),
    )
  }
  iam_additive = {
    for role in distinct(concat(
      keys(var.iam_additive),
      keys(local._group_iam_additive),
      keys(local._service_accounts_iam_additive),
      keys(local._service_identities_iam_additive),
    )) :
    role => concat(
      try(var.iam_additive[role], []),
      try(local._group_iam_additive[role], []),
      try(local._service_accounts_iam_additive[role], []),
      try(local._service_identities_iam_additive[role], []),
    )
  }
  labels = merge(
    coalesce(var.labels, {}), coalesce(try(var.defaults.labels, {}), {})
  )
  services = distinct(concat(var.services, local._services))
  vpc = coalesce(var.vpc, {
    host_project = null, gke_setup = null, subnets_iam = null
  })
  vpc_cloudservices = (
    local.vpc_gke_service_agent ||
    contains(var.services, "compute.googleapis.com")
  )
  vpc_gke_security_admin = coalesce(
    try(local.vpc.gke_setup.enable_security_admin, null), false
  )
  vpc_gke_service_agent = coalesce(
    try(local.vpc.gke_setup.enable_host_service_agent, null), false
  )
  vpc_subnet_bindings = {
    for binding in local._vpc_subnet_bindings :
    "${binding.subnet}:${binding.member}" => binding
  }
}

module "billing-alert" {
  for_each              = local.billing_alert == null ? {} : { 1 = 1 }
  source                = "../../../modules/billing-budget"
  billing_account       = local.billing_account_id
  name                  = "${module.project.project_id} budget"
  amount                = local.billing_alert.amount
  thresholds            = local.billing_alert.thresholds
  credit_treatment      = local.billing_alert.credit_treatment
  notification_channels = var.defaults.notification_channels
  projects              = ["projects/${module.project.number}"]
  email_recipients = {
    project_id = module.project.project_id
    emails     = local.essential_contacts
  }
}

module "dns" {
  source          = "../../../modules/dns"
  for_each        = toset(var.dns_zones)
  project_id      = coalesce(local.vpc.host_project, module.project.project_id)
  type            = "private"
  name            = each.value
  domain          = "${each.value}.${var.defaults.environment_dns_zone}"
  client_networks = [var.defaults.shared_vpc_self_link]
}

module "project" {
  source                     = "../../../modules/project"
  billing_account            = local.billing_account_id
  name                       = var.project_id
  prefix                     = var.prefix
  contacts                   = { for c in local.essential_contacts : c => ["ALL"] }
  iam                        = local.iam
  iam_additive               = local.iam_additive
  labels                     = local.labels
  org_policies               = try(var.org_policies, {})
  parent                     = var.folder_id
  service_encryption_key_ids = var.kms_service_agents
  services                   = local.services
  shared_vpc_service_config = var.vpc == null ? null : {
    host_project = local.vpc.host_project
    # these are non-authoritative
    service_identity_iam = {
      "roles/compute.networkUser" = compact([
        local.vpc_gke_service_agent ? "container-engine" : null,
        local.vpc_cloudservices ? "cloudservices" : null
      ])
      "roles/compute.securityAdmin" = compact([
        local.vpc_gke_security_admin ? "container-engine" : null,
      ])
      "roles/container.hostServiceAgentUser" = compact([
        local.vpc_gke_service_agent ? "container-engine" : null
      ])
    }
  }
}

module "service-accounts" {
  source     = "../../../modules/iam-service-account"
  for_each   = var.service_accounts
  name       = each.key
  project_id = module.project.project_id
  iam        = lookup(var.service_accounts_iam, each.key, null)
}

resource "google_compute_subnetwork_iam_member" "default" {
  for_each   = local.vpc_subnet_bindings
  project    = local.vpc.host_project
  subnetwork = "projects/${local.vpc.host_project}/regions/${each.value.region}/subnetworks/${each.value.subnet}"
  region     = each.value.region
  role       = "roles/compute.networkUser"
  member = (
    lookup(var.service_accounts, each.value.member, null) != null
    ? module.service-accounts[each.value.member].iam_email
    : each.value.member
  )
}
