#!/usr/bin/env bash

# Copyright 2026 PingCAP, Inc.
#
# 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.

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
test_dir="$(mktemp -d "${TMPDIR:-/tmp}/capacity-planner-kube-test.XXXXXX")"

cleanup() {
	rm -rf "$test_dir"
}
trap cleanup EXIT

fail() {
	echo "test/deploy-kube: $*" >&2
	exit 1
}

assert_contains() {
	local file="$1"
	local expected="$2"
	grep -Fq -- "$expected" "$file" ||
		fail "${file} does not contain: ${expected}"
}

mkdir -p "${test_dir}/bin"
export KIND_LOG="${test_dir}/kind.log"
export CAPTURED_KIND_CONFIG="${test_dir}/kind-config.yaml"
export KUBECTL_LOG="${test_dir}/kubectl.log"
export KUBECTL_MANIFEST_LOG="${test_dir}/manifests.yaml"
export CONTAINER_LOG="${test_dir}/container.log"
export CONTAINER_HOSTS_LOG="${test_dir}/container-hosts.log"

cat >"${test_dir}/bin/kind" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' "$*" >>"$KIND_LOG"

if [[ "${1:-}" == "get" && "${2:-}" == "clusters" ]]; then
	exit 0
fi

if [[ "${1:-}" == "create" && "${2:-}" == "cluster" ]]; then
	shift 2
	config=""
	kubeconfig=""
	while (($#)); do
		case "$1" in
		--config)
			config="$2"
			shift 2
			;;
		--kubeconfig)
			kubeconfig="$2"
			shift 2
			;;
		*)
			shift
			;;
		esac
	done
	cp "$config" "$CAPTURED_KIND_CONFIG"
	: >"$kubeconfig"
	exit 0
fi

if [[ "${1:-}" == "get" && "${2:-}" == "nodes" ]]; then
	printf '%s\n' \
		capacity-planner-control-plane \
		capacity-planner-worker \
		capacity-planner-worker2 \
		capacity-planner-worker3
	exit 0
fi

if [[ "${1:-}" == "delete" && "${2:-}" == "cluster" ]]; then
	exit 0
fi

echo "unexpected kind invocation: $*" >&2
exit 1
EOF

cat >"${test_dir}/bin/kubectl" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' "$*" >>"$KUBECTL_LOG"
joined=" $* "

if [[ "$joined" == *" create secret tls local-registry-tls "* ]]; then
	cat <<'MANIFEST'
apiVersion: v1
kind: Secret
metadata:
  name: local-registry-tls
type: kubernetes.io/tls
MANIFEST
	exit 0
fi
if [[ "$joined" == *" create configmap buildkitd-registry-ca "* ]]; then
	cat <<'MANIFEST'
apiVersion: v1
kind: ConfigMap
metadata:
  name: buildkitd-registry-ca
data:
  ca.crt: test
MANIFEST
	exit 0
fi
if [[ "$joined" == *" config set-cluster kind-capacity-planner "* ]]; then
	exit 0
fi

for argument in "$@"; do
	if [[ "$argument" == "apply" ]]; then
		printf '%s\n' '--- kubectl apply ---' >>"$KUBECTL_MANIFEST_LOG"
		if [[ "$*" == *" -f -"* ]]; then
			cat >>"$KUBECTL_MANIFEST_LOG"
		fi
		exit 0
	fi
done

if [[ "$joined" == *" get service local-registry "* && "$joined" == *"jsonpath={.spec.ports[0].nodePort}"* ]]; then
	exit 0
fi
if [[ "$joined" == *" get service local-registry "* && "$joined" == *"jsonpath={.status.loadBalancer.ingress[0].ip}"* ]]; then
	printf '172.18.255.245'
	exit 0
fi
if [[ "$joined" == *" wait "* || "$joined" == *" rollout status "* ]]; then
	exit 0
fi

echo "unexpected kubectl invocation: $*" >&2
exit 1
EOF

cat >"${test_dir}/bin/container-tool" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' "$*" >>"$CONTAINER_LOG"

if [[ "${1:-}" == "network" && "${2:-}" == "inspect" ]]; then
	printf '%s\n' '172.18.0.0/16'
	exit 0
fi
if [[ "${1:-}" == "inspect" ]]; then
	if [[ " $* " == *'index .HostConfig.PortBindings "6443/tcp"'* ]]; then
		printf '%s\n' '172.17.0.1 36443'
	else
		printf '%s\n' '6443/tcp'
	fi
	exit 0
fi
if [[ "${1:-}" == "exec" ]]; then
	if [[ "${2:-}" == "-i" ]]; then
		printf '%s\n' "--- $* ---" >>"$CONTAINER_HOSTS_LOG"
		cat >>"$CONTAINER_HOSTS_LOG"
	fi
	exit 0
fi

echo "unexpected container-tool invocation: $*" >&2
exit 1
EOF

chmod +x \
	"${test_dir}/bin/kind" \
	"${test_dir}/bin/kubectl" \
	"${test_dir}/bin/container-tool"

output_file="${test_dir}/output.log"
if ! KIND="${test_dir}/bin/kind" \
	KUBECTL="${test_dir}/bin/kubectl" \
	CONTAINER_TOOL="${test_dir}/bin/container-tool" \
	OPENSSL="openssl" \
	KIND_NODE_IMAGE="kindest/node:test" \
	KIND_CLUSTER_NAME="capacity-planner" \
	KIND_API_SERVER_ADDRESS="172.17.0.1" \
	KIND_API_SERVER_PORT="36443" \
	KIND_REGISTRY_IMAGE="registry:2" \
	KIND_REGISTRY_CERT_DIR="${test_dir}/registry-certs" \
	KIND_NETWORK="kind" \
	BUILDKIT_IMAGE="moby/buildkit:v0.31.2-rootless" \
	BUILDKIT_NAMESPACE="buildkit" \
	BUILDKIT_NAME="buildkitd" \
	METALLB_MANIFEST="https://example.invalid/metallb-native.yaml" \
	KUBECONFIG="${test_dir}/kubeconfig" \
	"${SCRIPT_DIR}/kube.sh" >"$output_file" 2>&1; then
	cat "$output_file" >&2
	fail "kube.sh failed"
fi

[[ "$(grep -c '^- role: control-plane$' "$CAPTURED_KIND_CONFIG")" -eq 1 ]] ||
	fail "Kind config must have one control-plane"
[[ "$(grep -c '^- role: worker$' "$CAPTURED_KIND_CONFIG")" -eq 3 ]] ||
	fail "Kind config must have three workers"
assert_contains "$CAPTURED_KIND_CONFIG" 'config_path = "/etc/containerd/certs.d"'
assert_contains "$CAPTURED_KIND_CONFIG" 'certSANs:'
assert_contains "$CAPTURED_KIND_CONFIG" '- "172.17.0.1"'
assert_contains "$CAPTURED_KIND_CONFIG" 'extraPortMappings:'
assert_contains "$CAPTURED_KIND_CONFIG" 'containerPort: 6443'
assert_contains "$CAPTURED_KIND_CONFIG" 'hostPort: 36443'
assert_contains "$CAPTURED_KIND_CONFIG" 'listenAddress: "172.17.0.1"'
if grep -Eq 'containerPort: 5000|hostPort: 5000' "$CAPTURED_KIND_CONFIG"; then
	fail "Kind config must not publish a registry host port"
fi
assert_contains "$KUBECTL_LOG" 'config set-cluster kind-capacity-planner --server=https://172.17.0.1:36443'

assert_contains "$KUBECTL_LOG" 'apply -f https://example.invalid/metallb-native.yaml'
assert_contains "$KUBECTL_MANIFEST_LOG" 'kind: IPAddressPool'
assert_contains "$KUBECTL_MANIFEST_LOG" 'kind: L2Advertisement'
assert_contains "$KUBECTL_MANIFEST_LOG" 'name: kind-registry'
assert_contains "$KUBECTL_MANIFEST_LOG" '172.18.255.245/32'
assert_contains "$KUBECTL_MANIFEST_LOG" 'name: kind-grafana'
assert_contains "$KUBECTL_MANIFEST_LOG" '172.18.255.244/32'
[[ "$(grep -c '^  autoAssign: false$' "$KUBECTL_MANIFEST_LOG")" -eq 2 ]] ||
	fail "registry and Grafana MetalLB pools must disable automatic assignment"
assert_contains "$KUBECTL_MANIFEST_LOG" 'kind: Deployment'
assert_contains "$KUBECTL_MANIFEST_LOG" 'namespace: local-registry'
assert_contains "$KUBECTL_MANIFEST_LOG" 'image: registry:2'
assert_contains "$KUBECTL_MANIFEST_LOG" 'type: Recreate'
assert_contains "$KUBECTL_MANIFEST_LOG" 'kind: Secret'
assert_contains "$KUBECTL_MANIFEST_LOG" 'name: local-registry-tls'
assert_contains "$KUBECTL_MANIFEST_LOG" 'REGISTRY_HTTP_TLS_CERTIFICATE'
assert_contains "$KUBECTL_MANIFEST_LOG" 'REGISTRY_HTTP_TLS_KEY'
assert_contains "$KUBECTL_MANIFEST_LOG" 'scheme: HTTPS'
assert_contains "$KUBECTL_MANIFEST_LOG" 'containerPort: 5000'
assert_contains "$KUBECTL_MANIFEST_LOG" 'path: /var/local-registry'
if grep -Fq 'hostPort:' "$KUBECTL_MANIFEST_LOG"; then
	fail "registry Deployment must not use hostPort"
fi
assert_contains "$KUBECTL_MANIFEST_LOG" 'kind: Service'
assert_contains "$KUBECTL_MANIFEST_LOG" 'type: LoadBalancer'
assert_contains "$KUBECTL_MANIFEST_LOG" 'allocateLoadBalancerNodePorts: false'
assert_contains "$KUBECTL_MANIFEST_LOG" 'metallb.io/loadBalancerIPs: "172.18.255.245"'
if grep -Fq 'nodePort:' "$KUBECTL_MANIFEST_LOG"; then
	fail "registry LoadBalancer must not allocate or declare a NodePort"
fi
assert_contains "$KUBECTL_MANIFEST_LOG" 'name: local-registry-hosting'
assert_contains "$KUBECTL_MANIFEST_LOG" 'host: "172.18.255.245:5000"'
assert_contains "$KUBECTL_MANIFEST_LOG" 'hostFromClusterNetwork: "172.18.255.245:5000"'

assert_contains "$KUBECTL_LOG" 'create configmap buildkitd-registry-ca --from-file=ca.crt='
assert_contains "$KUBECTL_LOG" '-n buildkit rollout status --timeout=5m deployment/buildkitd'
assert_contains "$KUBECTL_MANIFEST_LOG" 'name: buildkitd-config'
assert_contains "$KUBECTL_MANIFEST_LOG" 'namespace: buildkit'
assert_contains "$KUBECTL_MANIFEST_LOG" '[registry."172.18.255.245:5000"]'
assert_contains "$KUBECTL_MANIFEST_LOG" 'ca = ["/etc/buildkit/registry-ca/ca.crt"]'
assert_contains "$KUBECTL_MANIFEST_LOG" 'image: moby/buildkit:v0.31.2-rootless'
assert_contains "$KUBECTL_MANIFEST_LOG" '--oci-worker-no-process-sandbox'
assert_contains "$KUBECTL_MANIFEST_LOG" 'automountServiceAccountToken: false'
assert_contains "$KUBECTL_MANIFEST_LOG" 'runAsUser: 1000'
assert_contains "$KUBECTL_MANIFEST_LOG" 'appArmorProfile:'
assert_contains "$KUBECTL_MANIFEST_LOG" 'mountPath: /etc/buildkit/registry-ca'

assert_contains "$KUBECTL_LOG" 'get service local-registry -o jsonpath={.status.loadBalancer.ingress[0].ip}'
assert_contains "$KUBECTL_LOG" 'get service local-registry -o jsonpath={.spec.ports[0].nodePort}'
assert_contains "$CONTAINER_HOSTS_LOG" 'server = "https://172.18.255.245:5000"'
assert_contains "$CONTAINER_HOSTS_LOG" '[host."https://172.18.255.245:5000"]'
assert_contains "$CONTAINER_HOSTS_LOG" 'ca = "/etc/containerd/certs.d/172.18.255.245:5000/ca.crt"'
[[ "$(grep -c '^server = ' "$CONTAINER_HOSTS_LOG")" -eq 4 ]] ||
	fail "containerd registry config must be installed on all four nodes"
[[ "$(grep -c 'curl --noproxy \* --cacert /etc/containerd/certs.d/172.18.255.245:5000/ca.crt --fail --silent --show-error --connect-timeout 2 --max-time 3 https://172.18.255.245:5000/v2/' "$CONTAINER_LOG")" -eq 4 ]] ||
	fail "all four Kind nodes must verify the registry LoadBalancer address"
if grep -Eq '(^| )run( |$)' "$CONTAINER_LOG"; then
	fail "registry must not run as an external container"
fi
if grep -Fq 'delete cluster' "$KIND_LOG"; then
	fail "successful setup must keep the Kind cluster"
fi
assert_contains "$output_file" 'registry runs as local-registry/local-registry'
assert_contains "$output_file" 'API server: https://172.17.0.1:36443'
assert_contains "$output_file" 'registry LoadBalancer: 172.18.255.245:5000 (TLS verified'
assert_contains "$output_file" 'Grafana LoadBalancer: 172.18.255.244:80 (reserved for make deploy)'
assert_contains "$output_file" 'registry CA:'
assert_contains "$output_file" 'BuildKit: buildkit/buildkitd (moby/buildkit:v0.31.2-rootless)'
assert_contains "$output_file" 'make push builds inside Kind and pushes directly'
assert_contains "$output_file" '172.18.255.245:5000/NAME:TAG'

openssl verify -CAfile "${test_dir}/registry-certs/ca.crt" -verify_ip 172.18.255.245 \
	"${test_dir}/registry-certs/tls.crt" >/dev/null
openssl x509 -noout -ext subjectAltName -in "${test_dir}/registry-certs/tls.crt" |
	grep -Fq 'IP Address:172.18.255.245' || fail "registry certificate must contain the LoadBalancer IP SAN"

echo "test/deploy-kube: OK"
