#!/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)"
MODULE_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
temp_root="${TMPDIR:-/tmp}"
test_dir="$(mktemp -d "${temp_root%/}/capacity-planner-deploy-test.XXXXXX")"

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

fail() {
	echo "test/deploy: $*" >&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"
touch "${test_dir}/kubeconfig"
export DEPLOY_EVENT_LOG="${test_dir}/events.log"
export EXPECTED_KUBECTL="${test_dir}/bin/kubectl"

cat >"${test_dir}/bin/buildctl" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
resolved_kubectl="$(command -v kubectl)"
[[ -n "$resolved_kubectl" && "$resolved_kubectl" -ef "$EXPECTED_KUBECTL" ]] || {
	echo "buildctl did not receive the configured kubectl through PATH" >&2
	exit 1
}
printf 'buildctl KUBECONFIG=%s %s\n' "${KUBECONFIG:-}" "$*" >>"$DEPLOY_EVENT_LOG"
[[ "${FAIL_BUILDCTL:-0}" != "1" ]] || exit 17
EOF

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

joined=" $* "
if [[ "$joined" == *" get service local-registry "* && "$joined" == *"jsonpath={.status.loadBalancer.ingress[0].ip}"* ]]; then
	printf '172.18.255.245'
	exit 0
fi
if [[ "$joined" == *" -n buildkit get pod "* && "$joined" == *"app.kubernetes.io/name=buildkitd"* ]]; then
	printf 'buildkitd-6c8f7d9b9d-test'
	exit 0
fi
if [[ "$joined" == *" -n planner-system get deployment planner-release-capacity-planner "* ]]; then
	printf '[--config=/etc/capacity-planner/capacity-planner.yaml --tier-templates-config=/etc/capacity-planner/tier-templates.yaml]'
	exit 0
fi
if [[ "$joined" == *" -n planner-system get configmap "* && "$joined" == *"app.kubernetes.io/instance=planner-release"* ]]; then
	printf 'query:\n  bindAddress: :9090\n'
	exit 0
fi
if [[ "$joined" == *" -n metallb-system get ipaddresspool kind-grafana "* ]]; then
	printf '172.18.255.244/32'
	exit 0
fi
if [[ "$joined" == *" -n monitoring get deployment "* && "$joined" == *"app.kubernetes.io/instance=capacity-planner-grafana"* ]]; then
	printf 'capacity-planner-grafana'
	exit 0
fi
if [[ "$joined" == *" -n monitoring get service "* && "$joined" == *"app.kubernetes.io/instance=capacity-planner-grafana"* ]]; then
	printf 'capacity-planner-grafana'
	exit 0
fi
if [[ "$joined" == *" -n monitoring get service capacity-planner-grafana "* && "$joined" == *"jsonpath={.spec.type}"* ]]; then
	printf 'LoadBalancer'
	exit 0
fi
if [[ "$joined" == *" -n monitoring get service capacity-planner-grafana "* && "$joined" == *"jsonpath={.status.loadBalancer.ingress[0].ip}"* ]]; then
	printf '172.18.255.244'
	exit 0
fi
if [[ "$joined" == *" -n monitoring get service capacity-planner-grafana "* && "$joined" == *"jsonpath={.spec.allocateLoadBalancerNodePorts}"* ]]; then
	printf 'false'
	exit 0
fi
if [[ "$joined" == *" -n monitoring get service capacity-planner-grafana "* && "$joined" == *"jsonpath={.spec.ports[0].nodePort}"* ]]; then
	exit 0
fi
if [[ "$joined" == *" -n monitoring get configmap "* && "$joined" == *"dashboard-provider=default"* ]]; then
	printf '{"uid": "capacity-planner-metrics", "metrics": ["cpu", "memory", "pod_status_running", "pod_status_ready", "pod:tidb_server_query:rate1m"]}'
	exit 0
fi
if [[ "$joined" == *" -n monitoring get configmap "* && "$joined" == *"app.kubernetes.io/instance=capacity-planner-grafana"* ]]; then
	printf 'url: http://planner-release-capacity-planner-query.planner-system.svc.cluster.local:9090'
	exit 0
fi
if [[ "$joined" == *" get --raw /api/v1/namespaces/planner-system/services/http:planner-release-capacity-planner-query:9090/proxy/-/ready "* ]]; then
	printf 'Capacity Planner Query Server is Ready.'
	exit 0
fi
if [[ "$joined" == *" get --raw /api/v1/namespaces/monitoring/services/http:capacity-planner-grafana:80/proxy/api/health "* ]]; then
	printf '{"database":"ok"}'
	exit 0
fi
if [[ "$joined" == *" apply -f - "* ]]; then
	printf '%s\n' '--- stdin manifest ---' >>"$DEPLOY_EVENT_LOG"
	cat >>"$DEPLOY_EVENT_LOG"
	exit 0
fi
if [[ "$joined" == *" apply "* || "$joined" == *" wait "* || "$joined" == *" rollout status "* ]]; then
	exit 0
fi

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

cat >"${test_dir}/bin/helm" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
printf 'helm %s\n' "$*" >>"$DEPLOY_EVENT_LOG"
[[ "${FAIL_HELM:-0}" != "1" ]] || exit 17
while (($#)); do
	if [[ "$1" == "--values" ]]; then
		printf '%s\n' '--- helm values ---' >>"$DEPLOY_EVENT_LOG"
		cat "$2" >>"$DEPLOY_EVENT_LOG"
		exit 0
	fi
	shift
done
EOF

chmod +x \
	"${test_dir}/bin/buildctl" \
	"${test_dir}/bin/kubectl" \
	"${test_dir}/bin/helm"

output_file="${test_dir}/output.log"
if ! make -C "$MODULE_DIR" --no-print-directory \
	deploy/capacity-planner deploy/grafana testdeploy/capacity-planner \
	KUBECTL="${test_dir}/bin/kubectl" \
	BUILDCTL="${test_dir}/bin/buildctl" \
	HELM="${test_dir}/bin/helm" \
	E2E_KUBECONFIG="${test_dir}/kubeconfig" \
	IMAGE_NAME="planner-test" \
	IMAGE_TAG="test-tag" \
	VERSION="v1.2.3" \
	DEPLOY_RELEASE="planner-release" \
	DEPLOY_NAMESPACE="planner-system" \
	DEPLOY_TIMEOUT="90s" >"$output_file" 2>&1; then
	cat "$output_file" >&2
	fail "component deployment targets failed"
fi

assert_contains "$DEPLOY_EVENT_LOG" 'kubectl --kubeconfig'
assert_contains "$DEPLOY_EVENT_LOG" '-n local-registry get service local-registry'
assert_contains "$DEPLOY_EVENT_LOG" '-n buildkit get pod -l app.kubernetes.io/name=buildkitd'
assert_contains "$DEPLOY_EVENT_LOG" "buildctl KUBECONFIG=${test_dir}/kubeconfig"
assert_contains "$DEPLOY_EVENT_LOG" '--addr kube-pod://buildkitd-6c8f7d9b9d-test?context=kind-capacity-planner&namespace=buildkit&container=buildkitd build'
assert_contains "$DEPLOY_EVENT_LOG" '--frontend dockerfile.v0'
assert_contains "$DEPLOY_EVENT_LOG" "--local context=${MODULE_DIR}"
assert_contains "$DEPLOY_EVENT_LOG" "--local dockerfile=${MODULE_DIR}"
assert_contains "$DEPLOY_EVENT_LOG" '--opt filename=Dockerfile'
assert_contains "$DEPLOY_EVENT_LOG" '--opt build-arg:GIT_VERSION=v1.2.3'
assert_contains "$DEPLOY_EVENT_LOG" '--output type=image,name=172.18.255.245:5000/planner-test:test-tag,push=true'
if [[ -e "${test_dir}/capacity-planner.oci.tar" ]]; then
	fail "remote BuildKit must not return an OCI archive to the host"
fi

assert_contains "$DEPLOY_EVENT_LOG" 'apply -f'
assert_contains "$DEPLOY_EVENT_LOG" 'config/crd/bases/autoscaling.pingcap.com_autoscalers.yaml'
assert_contains "$DEPLOY_EVENT_LOG" 'wait --for=condition=Established --timeout=90s crd/autoscalers.autoscaling.pingcap.com'
assert_contains "$DEPLOY_EVENT_LOG" 'helm --kubeconfig'
assert_contains "$DEPLOY_EVENT_LOG" 'upgrade --install planner-release charts/capacity-planner'
assert_contains "$DEPLOY_EVENT_LOG" '--namespace planner-system --create-namespace'
assert_contains "$DEPLOY_EVENT_LOG" '--set-string image.repository=172.18.255.245:5000/planner-test'
assert_contains "$DEPLOY_EVENT_LOG" '--set-string image.tag=test-tag'
assert_contains "$DEPLOY_EVENT_LOG" '--set image.pullPolicy=Always'
assert_contains "$DEPLOY_EVENT_LOG" '--set-string config.query.bindAddress=:9090'
assert_contains "$DEPLOY_EVENT_LOG" '--wait --atomic --timeout 90s'
assert_contains "$DEPLOY_EVENT_LOG" '-n planner-system rollout status --timeout=90s deployment/planner-release-capacity-planner'
assert_contains "$output_file" 'deploy/capacity-planner: planner-system/planner-release-capacity-planner uses 172.18.255.245:5000/planner-test:test-tag'

assert_contains "$DEPLOY_EVENT_LOG" 'name: planner-release-capacity-planner-query'
assert_contains "$DEPLOY_EVENT_LOG" 'namespace: planner-system'
assert_contains "$DEPLOY_EVENT_LOG" 'type: ClusterIP'
assert_contains "$DEPLOY_EVENT_LOG" 'app.kubernetes.io/instance: planner-release'
assert_contains "$DEPLOY_EVENT_LOG" 'port: 9090'
assert_contains "$DEPLOY_EVENT_LOG" 'get --raw /api/v1/namespaces/planner-system/services/http:planner-release-capacity-planner-query:9090/proxy/-/ready'
assert_contains "$DEPLOY_EVENT_LOG" 'helm --kubeconfig'
assert_contains "$DEPLOY_EVENT_LOG" 'upgrade --install capacity-planner-grafana oci://ghcr.io/grafana-community/helm-charts/grafana'
assert_contains "$DEPLOY_EVENT_LOG" '--version 12.10.0 --namespace monitoring --create-namespace'
assert_contains "$DEPLOY_EVENT_LOG" '--post-renderer'
assert_contains "$DEPLOY_EVENT_LOG" '--set-file dashboards.default.capacity-planner.json='
assert_contains "$DEPLOY_EVENT_LOG" 'type: LoadBalancer'
assert_contains "$DEPLOY_EVENT_LOG" 'loadBalancerIP: 172.18.255.244'
assert_contains "$DEPLOY_EVENT_LOG" 'metallb.io/address-pool: kind-grafana'
assert_contains "$DEPLOY_EVENT_LOG" 'name: Capacity Planner'
assert_contains "$DEPLOY_EVENT_LOG" 'uid: capacity-planner'
assert_contains "$DEPLOY_EVENT_LOG" 'type: prometheus'
assert_contains "$DEPLOY_EVENT_LOG" 'url: http://planner-release-capacity-planner-query.planner-system.svc.cluster.local:9090'
assert_contains "$DEPLOY_EVENT_LOG" 'isDefault: true'
assert_contains "$DEPLOY_EVENT_LOG" 'dashboardProviders:'
assert_contains "$DEPLOY_EVENT_LOG" 'folder: Capacity Planner'
assert_contains "$DEPLOY_EVENT_LOG" 'path: /var/lib/grafana/dashboards/default'
assert_contains "$DEPLOY_EVENT_LOG" '-n monitoring rollout status --timeout=90s deployment/capacity-planner-grafana'
assert_contains "$DEPLOY_EVENT_LOG" '--for=jsonpath={.status.loadBalancer.ingress[0].ip}=172.18.255.244 service/capacity-planner-grafana'
assert_contains "$DEPLOY_EVENT_LOG" 'get --raw /api/v1/namespaces/monitoring/services/http:capacity-planner-grafana:80/proxy/api/health'
assert_contains "$output_file" 'deploy/grafana: monitoring/capacity-planner-grafana is ready'
assert_contains "$output_file" 'deploy/grafana: dashboard: /d/capacity-planner-metrics/capacity-planner-metrics'
assert_contains "$output_file" 'deploy/grafana: LoadBalancer URL: http://172.18.255.244'

assert_contains "$DEPLOY_EVENT_LOG" 'upgrade planner-release charts/capacity-planner'
assert_contains "$DEPLOY_EVENT_LOG" '--namespace planner-system --reuse-values'
assert_contains "$DEPLOY_EVENT_LOG" '--set-file tierTemplates.config='
assert_contains "$DEPLOY_EVENT_LOG" 'test/e2e/testdata/tier-templates.yaml'

assert_contains "${MODULE_DIR}/hack/grafana-dashboard.json" '"uid": "capacity-planner-metrics"'
assert_contains "${MODULE_DIR}/hack/grafana-dashboard.json" '"expr": "sum by (namespace, group, pod, container) (cpu{'
assert_contains "${MODULE_DIR}/hack/grafana-dashboard.json" 'memory{namespace=~'
assert_contains "${MODULE_DIR}/hack/grafana-dashboard.json" 'pod_status_running{namespace=~'
assert_contains "${MODULE_DIR}/hack/grafana-dashboard.json" 'pod_status_ready{namespace=~'
assert_contains "${MODULE_DIR}/hack/grafana-dashboard.json" 'pod:tidb_server_query:rate1m{namespace=~'
[[ "$(jq '[.panels[] | select((.title == "Pod Running" or .title == "Pod Ready") and .type == "timeseries")] | length' \
	"${MODULE_DIR}/hack/grafana-dashboard.json")" -eq 2 ]] ||
	fail "Pod Running and Pod Ready must both be time-series panels"
[[ "$(jq '[.panels[] | select(.title == "TiDB Query QPS" and .type == "timeseries")] | length' \
	"${MODULE_DIR}/hack/grafana-dashboard.json")" -eq 1 ]] ||
	fail "TiDB Query QPS must be a time-series panel"

post_rendered_file="${test_dir}/post-rendered.yaml"
"${MODULE_DIR}/hack/grafana-post-renderer.sh" >"$post_rendered_file" <<'EOF'
---
apiVersion: v1
kind: Service
metadata:
  name: capacity-planner-grafana
spec:
  type: LoadBalancer
EOF
assert_contains "$post_rendered_file" 'allocateLoadBalancerNodePorts: false'

push_line="$(grep -n -m1 '^buildctl ' "$DEPLOY_EVENT_LOG" | cut -d: -f1)"
deploy_line="$(grep -n -m1 '^helm ' "$DEPLOY_EVENT_LOG" | cut -d: -f1)"
grafana_line="$(grep -n 'oci://ghcr.io/grafana-community/helm-charts/grafana' "$DEPLOY_EVENT_LOG" | cut -d: -f1)"
testdeploy_line="$(grep -n '^helm .* upgrade planner-release charts/capacity-planner' "$DEPLOY_EVENT_LOG" | cut -d: -f1)"
[[ "$push_line" -lt "$deploy_line" ]] ||
	fail "expected in-cluster BuildKit push before Helm deploy"
[[ "$deploy_line" -lt "$grafana_line" ]] ||
	fail "expected capacity planner deploy before Grafana deploy"
[[ "$grafana_line" -lt "$testdeploy_line" ]] ||
	fail "expected E2E capacity planner config after the regular deployment"

if FAIL_BUILDCTL=1 make -C "$MODULE_DIR" --no-print-directory \
	push/capacity-planner \
	KUBECTL="${test_dir}/bin/kubectl" \
	BUILDCTL="${test_dir}/bin/buildctl" \
	E2E_KUBECONFIG="${test_dir}/kubeconfig" >/dev/null 2>&1; then
	fail "push/capacity-planner must propagate BuildKit failures"
fi

if FAIL_HELM=1 make -C "$MODULE_DIR" --no-print-directory \
	deploy/capacity-planner \
	KUBECTL="${test_dir}/bin/kubectl" \
	BUILDCTL="${test_dir}/bin/buildctl" \
	HELM="${test_dir}/bin/helm" \
	E2E_KUBECONFIG="${test_dir}/kubeconfig" \
	DEPLOY_RELEASE="planner-release" \
	DEPLOY_NAMESPACE="planner-system" >/dev/null 2>&1; then
	fail "deploy/capacity-planner must propagate Helm failures"
fi

echo "test/deploy: OK"
