data "snowflake_role" "sysadmin" {
  name = "SYSADMIN"
}

data "snowflake_role" "useradmin" {
  name = "USERADMIN"
}

# Create SNOWFLAKE_USAGE role.
resource "snowflake_role" "snowflake_usage_role" {
  name    = "SNOWFLAKE_USAGE"
  comment = "SNOWFLAKE_USAGE role"
}
resource "snowflake_grant_privileges_to_account_role" "snowflake_usage_imported_privileges" {
  account_role_name = snowflake_role.snowflake_usage_role.name
  privileges        = ["IMPORTED PRIVILEGES"]
  on_account_object {
    object_type = "DATABASE"
    object_name = "SNOWFLAKE"
  }
}

resource "snowflake_role" "roles" {
  for_each = { for role in var.roles : role.name => role }
  name     = each.key
  comment  = each.value.comment
}

resource "snowflake_role_ownership_grant" "roles" {
  for_each                      = { for role in var.roles : role.name => role }
  on_role_name                  = snowflake_role.roles[each.key].name
  to_role_name                  = each.value.owner
  revert_ownership_to_role_name = var.revert_ownership_to
}

locals {
  # Account roles
  role_grants_by_role_granted_to = merge(
    { for role in var.roles : role.name => role.granted_account_roles if role.granted_account_roles != null },
    # Grant all managed roles to SYSADMIN
    { (data.snowflake_role.sysadmin.name) = [for role in var.roles : role.name] }
  )
  # Map of grants where the key is the role ON which the grants are made, and the value is the list of roles TO which the role is made.
  role_grants_by_role_being_granted = transpose(local.role_grants_by_role_granted_to)

  # Database roles
  db_role_grants_list = flatten([
    for role in var.roles : [
      for db_role in role.granted_database_roles : {
        parent_role_name   = role.name,
        database_role_name = db_role,
      }
    ] if role.granted_database_roles != null
  ])

  db_role_grants_map = { for role_grant in local.db_role_grants_list : "${role_grant.parent_role_name}_${role_grant.database_role_name}" => role_grant }

  # [snowflake_grant_application_role]
  # The resource requires a dedicated pair application_role_name & parent_role_name for each resource
  application_roles_list = flatten([
    for role in var.roles : [
      for db_role in role.granted_application_roles : {
        parent_role_name      = role.name,
        application_role_name = db_role,
      }
    ] if try(role.granted_application_roles, null) != null
  ])

  application_roles_map = { for role_grant in local.application_roles_list : "${role_grant.parent_role_name}_${role_grant.application_role_name}" => role_grant }
}

resource "snowflake_role_grants" "roles" {
  for_each = local.role_grants_by_role_being_granted

  role_name              = each.key
  enable_multiple_grants = true
  roles                  = each.value

  depends_on = [snowflake_role.roles]
}

resource "snowflake_grant_database_role" "database_roles" {
  for_each = local.db_role_grants_map

  database_role_name = each.value.database_role_name
  parent_role_name   = snowflake_role.roles[each.value.parent_role_name].name
}

# grant application role to application
resource "snowflake_grant_application_role" "application_roles" {
  for_each = local.application_roles_map

  application_role_name    = each.value.application_role_name
  parent_account_role_name = snowflake_role.roles[each.value.parent_role_name].name
}

resource "snowflake_grant_privileges_to_account_role" "account_level_grants" {
  for_each = { for role in var.roles : role.name => role.account_level_grants if role.account_level_grants != null }

  provider          = snowflake.prod_atlantis
  privileges        = each.value
  account_role_name = snowflake_role.roles[each.key].name
  on_account        = true
}
