terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 4.58"
    }
    random = {
      source  = "hashicorp/random"
      version = "~> 3.6"
    }
  }
}

variable "env" {
  type = string
}

variable "cloud_region" {
  type = string
}

variable "logical_region_name" {
  type = string
}

variable "cluster_name" {
  type = string
}

variable "resource_group_name" {
  type = string
}

variable "virtual_network_id" {
  type = string
}

variable "private_endpoint_subnet_id" {
  type = string
}

variable "kubeconfig_path" {
  type = string
}

variable "region_resource_group_name" {
  type = string
}

variable "region_mysql_server_name" {
  type = string
}

variable "region_key_vault_name" {
  type = string
}

variable "region_admin_password_secret_name" {
  type = string
}

variable "tags" {
  type    = map(string)
  default = {}
}

locals {
  db_logical_region_name = replace(var.logical_region_name, "-", "_")
  cluster_service_db     = "${var.env}_${local.db_logical_region_name}_cluster_service"
  config_center_db       = "${var.env}_${local.db_logical_region_name}_config_center"
  cluster_service_user   = "cluster_serivce_${substr(md5(local.cluster_service_db), 0, 10)}"
  config_center_user     = "config_center_${substr(md5(local.config_center_db), 0, 10)}"
  cluster_service_secret = "${var.env}-${var.logical_region_name}-rds-cluster-service-pawssword"
  config_center_secret   = "${var.env}-${var.logical_region_name}-rds-config-center-pawssword"
  cluster_service_pod    = "mysql-client-${replace(local.cluster_service_db, "_", "-")}"
  config_center_pod      = "mysql-client-${replace(local.config_center_db, "_", "-")}"
  private_mysql_fqdn     = "${data.azurerm_mysql_flexible_server.region.name}.${azurerm_private_dns_zone.mysql.name}"
  cluster_service = {
    host                 = local.private_mysql_fqdn
    port                 = 3306
    db                   = azurerm_mysql_flexible_database.cluster_service.name
    username             = local.cluster_service_user
    password_secret_name = azurerm_key_vault_secret.cluster_service.name
  }
  config_center = {
    host                 = local.private_mysql_fqdn
    port                 = 3306
    db                   = azurerm_mysql_flexible_database.config_center.name
    username             = local.config_center_user
    password_secret_name = azurerm_key_vault_secret.config_center.name
  }
}

data "azurerm_mysql_flexible_server" "region" {
  name                = var.region_mysql_server_name
  resource_group_name = var.region_resource_group_name
}

data "azurerm_key_vault" "region" {
  name                = var.region_key_vault_name
  resource_group_name = var.region_resource_group_name
}

data "azurerm_key_vault_secret" "admin" {
  name         = var.region_admin_password_secret_name
  key_vault_id = data.azurerm_key_vault.region.id
}

resource "azurerm_private_dns_zone" "mysql" {
  name                = "privatelink.mysql.database.azure.com"
  resource_group_name = var.resource_group_name
  tags                = var.tags
}

resource "azurerm_private_dns_zone_virtual_network_link" "mysql" {
  name                  = "${var.cluster_name}-mysql-dns-link"
  resource_group_name   = var.resource_group_name
  private_dns_zone_name = azurerm_private_dns_zone.mysql.name
  virtual_network_id    = var.virtual_network_id
  tags                  = var.tags
}

resource "azurerm_private_endpoint" "mysql" {
  name                = "${var.cluster_name}-mysql-pe"
  location            = var.cloud_region
  resource_group_name = var.resource_group_name
  subnet_id           = var.private_endpoint_subnet_id
  tags                = var.tags

  private_service_connection {
    name                           = "${var.cluster_name}-mysql-connection"
    private_connection_resource_id = data.azurerm_mysql_flexible_server.region.id
    subresource_names              = ["mysqlServer"]
    is_manual_connection           = false
  }

  private_dns_zone_group {
    name                 = "${var.cluster_name}-dns-zone-group"
    private_dns_zone_ids = [azurerm_private_dns_zone.mysql.id]
  }
}

resource "random_password" "cluster_service" {
  length           = 16
  special          = true
  override_special = "!@#$%^&*()_+-="
}

resource "random_password" "config_center" {
  length           = 16
  special          = true
  override_special = "!@#$%^&*()_+-="
}

resource "azurerm_key_vault_secret" "cluster_service" {
  name = local.cluster_service_secret
  value = jsonencode({
    username = local.cluster_service_user
    password = random_password.cluster_service.result
  })
  key_vault_id = data.azurerm_key_vault.region.id
  tags         = var.tags
}

resource "azurerm_key_vault_secret" "config_center" {
  name = local.config_center_secret
  value = jsonencode({
    username = local.config_center_user
    password = random_password.config_center.result
  })
  key_vault_id = data.azurerm_key_vault.region.id
  tags         = var.tags
}

resource "azurerm_mysql_flexible_database" "cluster_service" {
  name                = local.cluster_service_db
  resource_group_name = data.azurerm_mysql_flexible_server.region.resource_group_name
  server_name         = data.azurerm_mysql_flexible_server.region.name
  charset             = "utf8mb4"
  collation           = "utf8mb4_general_ci"
}

resource "azurerm_mysql_flexible_database" "config_center" {
  name                = local.config_center_db
  resource_group_name = data.azurerm_mysql_flexible_server.region.resource_group_name
  server_name         = data.azurerm_mysql_flexible_server.region.name
  charset             = "utf8mb4"
  collation           = "utf8mb4_general_ci"
}

resource "null_resource" "cluster_service_user" {
  triggers = {
    db_name                   = azurerm_mysql_flexible_database.cluster_service.name
    username                  = local.cluster_service_user
    password                  = random_password.cluster_service.result
    host                      = local.private_mysql_fqdn
    kubeconfig                = var.kubeconfig_path
    bootstrap_command_version = "stdin-v1"
  }

  provisioner "local-exec" {
    interpreter = ["bash", "-c"]
    environment = {
      KUBECONFIG = var.kubeconfig_path
      MYSQL_HOST = self.triggers.host
      MYSQL_USER = jsondecode(data.azurerm_key_vault_secret.admin.value).username
      MYSQL_PASS = jsondecode(data.azurerm_key_vault_secret.admin.value).password
      DB_NAME    = self.triggers.db_name
      APP_USER   = self.triggers.username
      APP_PASS   = self.triggers.password
      POD_NAME   = local.cluster_service_pod
    }
    command = <<-EOT
      set -euo pipefail
      kubectl -n default delete pod "$POD_NAME" --ignore-not-found >/dev/null 2>&1 || true
      trap 'kubectl -n default delete pod "$POD_NAME" --ignore-not-found >/dev/null 2>&1 || true' EXIT
      kubectl -n default run "$POD_NAME" --image=mysql:8.0 --restart=Never --command -- sleep 3600 >/dev/null
      kubectl -n default wait --for=condition=Ready --timeout=300s "pod/$POD_NAME" >/dev/null
      kubectl -n default exec -i "$POD_NAME" -- mysql -h "$MYSQL_HOST" -u "$MYSQL_USER" -p"$MYSQL_PASS" --ssl-mode=REQUIRED <<SQL
CREATE USER IF NOT EXISTS '$APP_USER'@'%' IDENTIFIED BY '$APP_PASS';
ALTER USER '$APP_USER'@'%' IDENTIFIED BY '$APP_PASS';
GRANT ALL PRIVILEGES ON $DB_NAME.* TO '$APP_USER'@'%';
FLUSH PRIVILEGES;
SQL
    EOT
  }

  depends_on = [
    azurerm_private_endpoint.mysql,
    azurerm_mysql_flexible_database.cluster_service,
    azurerm_private_dns_zone_virtual_network_link.mysql,
  ]
}

resource "null_resource" "config_center_user" {
  triggers = {
    db_name                   = azurerm_mysql_flexible_database.config_center.name
    username                  = local.config_center_user
    password                  = random_password.config_center.result
    host                      = local.private_mysql_fqdn
    kubeconfig                = var.kubeconfig_path
    bootstrap_command_version = "stdin-v1"
  }

  provisioner "local-exec" {
    interpreter = ["bash", "-c"]
    environment = {
      KUBECONFIG = var.kubeconfig_path
      MYSQL_HOST = self.triggers.host
      MYSQL_USER = jsondecode(data.azurerm_key_vault_secret.admin.value).username
      MYSQL_PASS = jsondecode(data.azurerm_key_vault_secret.admin.value).password
      DB_NAME    = self.triggers.db_name
      APP_USER   = self.triggers.username
      APP_PASS   = self.triggers.password
      POD_NAME   = local.config_center_pod
    }
    command = <<-EOT
      set -euo pipefail
      kubectl -n default delete pod "$POD_NAME" --ignore-not-found >/dev/null 2>&1 || true
      trap 'kubectl -n default delete pod "$POD_NAME" --ignore-not-found >/dev/null 2>&1 || true' EXIT
      kubectl -n default run "$POD_NAME" --image=mysql:8.0 --restart=Never --command -- sleep 3600 >/dev/null
      kubectl -n default wait --for=condition=Ready --timeout=300s "pod/$POD_NAME" >/dev/null
      kubectl -n default exec -i "$POD_NAME" -- mysql -h "$MYSQL_HOST" -u "$MYSQL_USER" -p"$MYSQL_PASS" --ssl-mode=REQUIRED <<SQL
CREATE USER IF NOT EXISTS '$APP_USER'@'%' IDENTIFIED BY '$APP_PASS';
ALTER USER '$APP_USER'@'%' IDENTIFIED BY '$APP_PASS';
GRANT ALL PRIVILEGES ON $DB_NAME.* TO '$APP_USER'@'%';
FLUSH PRIVILEGES;
SQL
    EOT
  }

  depends_on = [
    azurerm_private_endpoint.mysql,
    azurerm_mysql_flexible_database.config_center,
    azurerm_private_dns_zone_virtual_network_link.mysql,
  ]
}

output "cluster_service" {
  value = local.cluster_service
}

output "config_center" {
  value = local.config_center
}
