terraform {
  required_version = ">= 1.5"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
    google = {
      source  = "hashicorp/google"
      version = "~> 5.0"
    }
  }

  backend "s3" {
    # Configured via -backend-config or backend.hcl.
    # bucket         = "helix-terraform-state-<account-id>"
    # key            = "infra/gcp-dax/terraform.tfstate"
    # region         = "ap-southeast-1"
    # dynamodb_table = "helix-terraform-lock"
    # encrypt        = true
  }
}

provider "aws" {
  region = var.aws_region
}

provider "google" {
  project = var.gcp_project_id
}

locals {
  dataset_inventory_file = abspath("${path.module}/${var.dataset_inventory_path}")
  dataset_inventory      = jsondecode(file(local.dataset_inventory_file))
  dataset_rows           = try(local.dataset_inventory.datasets, [])

  # Provider controller scope is deliberately wider than new-request
  # admission. A dataset becoming non-requestable must not remove the DAX
  # controller's ability to observe or revoke historical grants. Only an
  # explicit catalog lifecycle transition away from active may remove the
  # dataset-level controller binding.
  managed_dataset_rows = [
    for dataset in local.dataset_rows : dataset
    if try(dataset.status, "active") == "active"
  ]
  managed_datasets = {
    for dataset in local.managed_dataset_rows :
    "${dataset.project_id}.${dataset.dataset_id}" => dataset
  }
  managed_dataset_locators = sort(keys(local.managed_datasets))

  service_account_json_ssm_parameter_name = (
    var.service_account_json_ssm_parameter_name != ""
    ? var.service_account_json_ssm_parameter_name
    : "/${var.project}/${var.environment}/DAX_BIGQUERY_SERVICE_ACCOUNT_JSON"
  )
}

resource "terraform_data" "seed_validation" {
  input = {
    managed_dataset_count        = length(local.managed_datasets)
    managed_dataset_scope_digest = sha256(join("\n", local.managed_dataset_locators))
  }

  lifecycle {
    precondition {
      condition     = contains(var.managed_dataset_project_ids, var.gcp_project_id)
      error_message = "managed_dataset_project_ids must include the primary gcp_project_id."
    }

    precondition {
      condition = alltrue([
        for dataset in local.managed_dataset_rows :
        contains(var.managed_dataset_project_ids, dataset.project_id)
      ])
      error_message = "All active DAX catalog datasets must belong to an explicitly allowed project."
    }

    precondition {
      condition     = length(local.managed_dataset_rows) == length(local.managed_datasets)
      error_message = "Active DAX catalog datasets must have unique project_id.dataset_id locators."
    }

    precondition {
      condition = alltrue([
        for dataset in local.managed_dataset_rows :
        length(dataset.dataset_id) >= 1
        && length(dataset.dataset_id) <= 1024
        && can(regex("^[A-Za-z0-9_]+$", dataset.dataset_id))
      ])
      error_message = "Active DAX catalog datasets must use valid BigQuery dataset ids."
    }

    precondition {
      condition = alltrue([
        for dataset in local.managed_dataset_rows :
        can(regex("^[a-z][a-z0-9-]{4,28}[a-z0-9]$", dataset.project_id))
      ])
      error_message = "Active DAX catalog datasets must use canonical GCP project ids."
    }

    precondition {
      condition     = length(local.managed_datasets) > 0
      error_message = "DAX seed must contain at least one active catalog dataset."
    }
  }
}

resource "google_service_account" "dax_bigquery" {
  account_id   = var.service_account_id
  display_name = var.service_account_display_name
  description  = "Helix DAX BigQuery dataset IAM reconciler. Dataset scope is managed from deploy/data-access/datasets seed JSON through Terraform."
}

resource "google_project_iam_custom_role" "dax_dataset_iam_reconciler" {
  for_each = var.managed_dataset_project_ids

  project     = each.value
  role_id     = var.custom_role_id
  title       = "Helix DAX Dataset IAM Reconciler"
  description = "Least-privilege role for Helix DAX dataset ACL management and schema metadata reads."
  permissions = [
    "bigquery.datasets.get",
    "bigquery.datasets.getIamPolicy",
    "bigquery.datasets.update",
    "bigquery.datasets.setIamPolicy",
    "bigquery.tables.get",
    "bigquery.tables.getIamPolicy",
    "bigquery.tables.list",
    "bigquery.tables.setIamPolicy",
  ]
}

resource "google_bigquery_dataset_iam_member" "dax_managed_dataset" {
  for_each = local.managed_datasets

  project    = each.value.project_id
  dataset_id = each.value.dataset_id
  role       = google_project_iam_custom_role.dax_dataset_iam_reconciler[each.value.project_id].name
  member     = "serviceAccount:${google_service_account.dax_bigquery.email}"
}

moved {
  from = google_project_iam_custom_role.dax_dataset_iam_reconciler
  to   = google_project_iam_custom_role.dax_dataset_iam_reconciler["gcp-digital-bi"]
}

resource "google_service_account_key" "dax_bigquery" {
  service_account_id = google_service_account.dax_bigquery.name
}

resource "aws_ssm_parameter" "dax_bigquery_service_account_json" {
  name        = local.service_account_json_ssm_parameter_name
  description = "Helix DAX data_access/bigquery service account JSON generated by Terraform."
  type        = "SecureString"
  value       = base64decode(google_service_account_key.dax_bigquery.private_key)
  overwrite   = true

  tags = {
    Project     = var.project
    Environment = var.environment
    ManagedBy   = "terraform"
    Component   = "data-access"
  }
}
