# =============================================================================
# ACK Managed Kubernetes Cluster
# NOTE: "关闭托管模式" here refers to disabling automatic (managed) upgrades.
# The cluster itself is ACK Pro (managed control plane) which is the standard.
# =============================================================================

resource "alicloud_cs_managed_kubernetes" "this" {
  depends_on   = [alicloud_nat_gateway.this]
  name         = local.cluster_name
  cluster_spec = "ack.pro.small"
  # Single-zone node vswitch (same AZ as pod vswitch) — Regional arch optimises same-AZ traffic
  vswitch_ids       = [alicloud_vswitch.node[0].id]
  pod_vswitch_ids   = [alicloud_vswitch.pod.id]
  service_cidr      = var.service_cidr
  security_group_id = alicloud_security_group.this.id

  enable_rrsa         = true
  deletion_protection = false
  proxy_mode          = "ipvs"

  # Disable automatic upgrade (关闭自动升级)
  maintenance_window {
    enable = false
  }

  # API Server is automatically reachable via public EIP on ACK Pro clusters.
  # The kubeconfig data source fetches the public endpoint (PrivateIpAddress=false).

  tags = local.common_tags

  # -- Addons --
  addons {
    name = "terway-eniip"
  }
  addons {
    name = "ack-pod-identity-webhook"
  }
  addons {
    name = "ack-ram-authenticator"
    config = jsonencode({
      EnableNonBootstrapMapping = true
    })
  }
  addons {
    name    = "managed-aliyun-acr-credential-helper"
    version = "v24.01.29.1-5318af4-aliyun"
    config = jsonencode({
      AcrInstanceInfo = [{
        instanceId = local.cr_instance_id
        regionId   = local.cr_region
      }]
      watchNamespace    = "all"
      enableRRSA        = false
      serviceAccount    = "*"
      expiringThreshold = "15m"
    })
  }
  addons {
    name = "alb-ingress-controller"
    config = jsonencode({
      albIngress = {
        CreateDefaultALBConfig = false
      }
    })
  }
}

# =============================================================================
# Worker Node Pool  (关闭托管 = unmanaged / manual node pool)
# =============================================================================

resource "alicloud_cs_kubernetes_node_pool" "worker" {
  cluster_id     = alicloud_cs_managed_kubernetes.this.id
  node_pool_name = "worker-nodepool"

  # Spread across all 3 node vswitches for availability
  vswitch_ids = alicloud_vswitch.node[*].id
  # ESS (autoscaling) only supports a single instance type per node pool
  instance_types = [var.worker_instance_type]

  system_disk_category = "cloud_essd"
  system_disk_size     = 100

  data_disks {
    category = "cloud_essd"
    size     = 200
  }

  multi_az_policy    = "BALANCE"
  security_group_ids = [alicloud_security_group.this.id]

  # Pay-as-you-go (按量付费)
  instance_charge_type = "PostPaid"

  # Node labels
  labels {
    key   = "node_type"
    value = "worker"
  }

  # Unmanaged node pool (关闭托管)
  management {
    enable      = false
    auto_repair = false
  }

  # Auto-scaling configuration (自动伸缩)
  # 策略: 默认策略 | 允许缩容: true | 缩容阈值: 50% | 缩容触发时延: 10min
  scaling_config {
    enable      = true
    min_size    = var.worker_min_size
    max_size    = var.worker_max_size
    is_bond_eip = false
  }
}

# =============================================================================
# Cluster Autoscaler Configuration
# Corresponds to "节点伸缩配置" settings shown in the ACK console screenshot:
#   - 节点伸缩方案: 自动伸缩 (cluster autoscaler, not instant elasticity)
#   - 缩容阈值: 50%
#   - GPU 缩容阈值: 30%
#   - 缩容触发时延: 10 分钟
# =============================================================================

resource "alicloud_cs_autoscaling_config" "this" {
  cluster_id = alicloud_cs_managed_kubernetes.this.id

  scale_down_enabled        = true
  expander                  = "least-waste" # 默认策略: 选择资源浪费最少的节点池
  utilization_threshold     = "0.5"         # 缩容阈值 50%
  gpu_utilization_threshold = "0.3"         # GPU 缩容阈值 30%
  cool_down_duration        = "10m"         # 缩容触发时延 10 分钟
  scan_interval             = "30s"
  unneeded_duration         = "10m"
}

# =============================================================================
# RAM Permissions: grant cluster-admin to specified RAM roles
# =============================================================================

resource "alicloud_cs_kubernetes_permissions" "admin" {
  count = length(var.admin_ram_ids)
  uid   = var.admin_ram_ids[count.index]
  permissions {
    cluster     = alicloud_cs_managed_kubernetes.this.id
    role_type   = "cluster"
    role_name   = "admin"
    namespace   = ""
    is_custom   = false
    is_ram_role = true
  }
}

# =============================================================================
# SLB ACL: restrict API Server access to known CIDRs
# =============================================================================

resource "alicloud_slb_acl" "api_server" {
  name       = "${local.cluster_name}-slb-acl"
  ip_version = "ipv4"
}

resource "alicloud_slb_acl_entry_attachment" "api_server" {
  # Allow Alibaba internal traffic (100.104.0.0/16), the cluster VPC, and caller-supplied CIDRs
  for_each = toset(concat(["100.104.0.0/16", var.vpc_cidr], var.public_access_cidrs))
  acl_id   = alicloud_slb_acl.api_server.id
  entry    = each.key
}

resource "null_resource" "api_server_acl_attach" {
  count = length(var.public_access_cidrs) > 0 ? 1 : 0

  triggers = {
    slb_id  = alicloud_cs_managed_kubernetes.this.slb_id
    acl_id  = alicloud_slb_acl.api_server.id
    profile = local.alicloud_account
    region  = var.cloud_region
  }

  provisioner "local-exec" {
    when    = create
    command = <<-EOT
      aliyun slb SetLoadBalancerTCPListenerAttribute \
        --profile ${self.triggers.profile} \
        --region ${self.triggers.region} \
        --LoadBalancerId ${self.triggers.slb_id} \
        --ListenerPort 6443 \
        --AclStatus 'on' \
        --AclType white \
        --AclId ${self.triggers.acl_id}
    EOT
  }

  provisioner "local-exec" {
    when    = destroy
    command = <<-EOT
      aliyun slb SetLoadBalancerTCPListenerAttribute \
        --profile ${self.triggers.profile} \
        --region ${self.triggers.region} \
        --LoadBalancerId ${self.triggers.slb_id} \
        --ListenerPort 6443 \
        --AclStatus 'off'
    EOT
  }
}

# =============================================================================
# Helm: ack-secret-manager
# =============================================================================

resource "helm_release" "ack_secret_manager" {
  depends_on       = [alicloud_cs_kubernetes_node_pool.worker]
  name             = "ack-secret-manager"
  repository       = "https://aliacs-app-catalog.oss-cn-hangzhou.aliyuncs.com/charts-incubator/"
  chart            = "ack-secret-manager"
  version          = "0.5.12"
  namespace        = "kube-system"
  create_namespace = false

  disable_openapi_validation = true

  set {
    name  = "clusterProfile"
    value = "Default"
  }
  set {
    name  = "clusterType"
    value = "ManagedKubernetes"
  }
  set {
    name  = "image.repositoryInternal"
    value = "registry-vpc.${var.cloud_region}.aliyuncs.com/acs/ack-secret-manager"
  }
  set {
    name  = "image.repositoryInternet"
    value = "registry-${var.cloud_region}.ack.aliyuncs.com/acs/ack-secret-manager"
  }
  set {
    name  = "cleanupImage.repositoryInternal"
    value = "registry-vpc.${var.cloud_region}.aliyuncs.com/acs/ack-secret-manager-cleanup"
  }
  set {
    name  = "cleanupImage.repositoryInternet"
    value = "registry-${var.cloud_region}.ack.aliyuncs.com/acs/ack-secret-manager-cleanup"
  }
  set {
    name  = "command.region"
    value = var.cloud_region
  }
}

# =============================================================================
# Data sources used in outputs
# =============================================================================

data "alicloud_nat_gateways" "this" {
  depends_on = [alicloud_cs_kubernetes_permissions.admin]
  vpc_id     = alicloud_vpc.this.id
}

data "external" "kubeconfig" {
  program = ["bash", "-c", <<-EOT
    kube_config=$(aliyun cs DescribeClusterUserKubeconfig \
      --profile ${local.alicloud_account} \
      --region ${var.cloud_region} \
      --ClusterId ${alicloud_cs_managed_kubernetes.this.id} \
      --PrivateIpAddress false | jq -r '.config')
    server=$(echo "$kube_config" | yq -r '.clusters[0].cluster.server')
    ca=$(echo "$kube_config" | yq -r '.clusters[0].cluster.certificate-authority-data')
    key=$(echo "$kube_config" | yq -r '.users[0].user.client-key-data')
    cert=$(echo "$kube_config" | yq -r '.users[0].user.client-certificate-data')
    kubeconfig_b64=$(echo -n "$kube_config" | base64 -w 0)
    cat <<EOF
    {
      "server": "$${server}",
      "ca": "$${ca}",
      "key": "$${key}",
      "cert": "$${cert}",
      "kubeconfig_b64": "$${kubeconfig_b64}"
    }
    EOF
    EOT
  ]

  lifecycle {
    postcondition {
      condition     = self.result.server != ""
      error_message = "server is empty"
    }
    postcondition {
      condition     = self.result.ca != ""
      error_message = "ca is empty"
    }
    postcondition {
      condition     = self.result.key != ""
      error_message = "key is empty"
    }
    postcondition {
      condition     = self.result.cert != ""
      error_message = "cert is empty"
    }
    postcondition {
      condition     = self.result.kubeconfig_b64 != ""
      error_message = "kubeconfig is empty"
    }
  }
}
# =============================================================================
# Worker Node RAM Role — attach pre-existing policies
# =============================================================================

locals {
  worker_node_policies = toset([
    "aliyun-ksm-ack-policy",
    "regional-server-iam",
  ])
}

resource "alicloud_ram_role_policy_attachment" "worker_node" {
  for_each    = local.worker_node_policies
  role_name   = alicloud_cs_kubernetes_node_pool.worker.ram_role_name
  policy_name = each.value
  policy_type = "Custom"
}

# =============================================================================
# Fixed admin RAM roles (e.g. CICD pipeline)
# =============================================================================

locals {
  admin_ram_roles = ["cicd-role", "AliyunReservedSSO-admin", "AliyunReservedSSO-poweruser"]
}

data "alicloud_ram_roles" "ack_admin" {
  for_each   = toset(local.admin_ram_roles)
  name_regex = "^${each.key}$"
}

resource "alicloud_cs_kubernetes_permissions" "ack_admin" {
  for_each = toset(local.admin_ram_roles)
  uid      = data.alicloud_ram_roles.ack_admin[each.key].roles[0].id
  permissions {
    cluster     = alicloud_cs_managed_kubernetes.this.id
    role_type   = "cluster"
    role_name   = "admin"
    namespace   = ""
    is_custom   = false
    is_ram_role = true
  }
}

# =============================================================================
# AWS OIDC Provider for ACK
# =============================================================================

resource "aws_iam_openid_connect_provider" "ack" {
  url = alicloud_cs_managed_kubernetes.this.rrsa_metadata[0].rrsa_oidc_issuer_url

  client_id_list = ["observability.pingcap.com"]

  # Alibaba Cloud OIDC thumbprint (fixed)
  thumbprint_list = ["9e99a48a9960b14926bb7f3b02e22da2b0ab7280"]

  tags = local.common_tags
}
