#!/usr/bin/env bash
# Deploy or roll back Bloom on the dev k3s cluster via Helm over SSH (M24-5) - the
# Kubernetes-native successor to the compose target (exedev.sh), preserving its
# build-once/deploy-many semantics: CI builds the SHA-tagged image once and this script deploys
# exactly that tag; `helm rollback` replaces the .deployed_tag_prev bookkeeping (helm keeps the
# revision history in-cluster). Compose stays the live path (exe.dev edge -> 8080) until the
# documented cutover flips the edge to Traefik:80 - runbook in docs/deployment.md.
#
# Modes (MODE env, default "deploy"):
#   deploy   - refresh the ghcr-pull imagePullSecret with the CI job token, ensure the pinned
#              helm CLI on the VM, stream the chart, then `helm upgrade --install` pinned to
#              BLOOM_IMAGE_TAG with the dev overlay. Probe-gated (--wait) + endpoint-gated.
#   rollback - `helm rollback` to HELM_REVISION (default: the previous revision), through the
#              same --wait + endpoint health gate. The pull secret is refreshed first so a
#              node that evicted the old image can re-pull it.
#
# Requires: DEPLOY_HOST, DEPLOY_SSH_KEY, REGISTRY_USER, REGISTRY_TOKEN (all modes);
# BLOOM_IMAGE_TAG (deploy only). Optional: DEPLOY_USER (exedev), BLOOM_NAMESPACE (bloom),
# HELM_RELEASE (bloom), HELM_TIMEOUT (5m0s), HELM_REVISION (rollback; empty = previous),
# HEALTH_HOST (bloom-server.exe.xyz - the ingress host the endpoint check routes by).
# Run from the repo root.
#
# exe.dev specifics: no scp/sftp - the helper scripts and the chart tarball stream over
# `bash -s` / `cat` exec channels, exactly like bootstrap-k3s.sh and the secrets sync.
set -euo pipefail

mode="${MODE:-deploy}"
case "$mode" in
  deploy) : "${BLOOM_IMAGE_TAG:?BLOOM_IMAGE_TAG not set (the SHA-tagged image built by CI)}" ;;
  rollback) ;;
  *) echo "::error::MODE must be deploy|rollback (got '$mode')" >&2; exit 1 ;;
esac
: "${REGISTRY_USER:?REGISTRY_USER not set}"
: "${REGISTRY_TOKEN:?REGISTRY_TOKEN not set}"
namespace="${BLOOM_NAMESPACE:-bloom}"
release="${HELM_RELEASE:-bloom}"
timeout="${HELM_TIMEOUT:-5m0s}"
health_host="${HEALTH_HOST:-bloom-server.exe.xyz}"
# Admin dashboard origin resolved from Cloudflare at deploy time (#535, resolve-admin-origin.sh).
# Empty = the lookup fell back, so we leave the git-visible values-dev.yaml default in place.
admin_origin="${BLOOM_ADMIN_DASHBOARD_URL:-}"

# shellcheck source=scripts/deploy/sshlib.sh
source "$(dirname "${BASH_SOURCE[0]}")/sshlib.sh"
deploy_ssh_setup
chart_dir="${HELM_CHART_DIR:-/home/${deploy_user}/.bloom-helm}"

echo "==> Refreshing the ${namespace}/ghcr-pull imagePullSecret with the CI job token"
"${ssh_cmd[@]}" "GHCR_USER='$REGISTRY_USER' GHCR_TOKEN='$REGISTRY_TOKEN' BLOOM_NAMESPACE='$namespace' bash -s -- ghcr-secret" \
  < deploy/k8s/bootstrap-k3s.sh

echo "==> Ensuring the pinned helm CLI on $DEPLOY_HOST (idempotent)"
"${ssh_cmd[@]}" 'bash -s -- install' < deploy/k8s/helm-cli.sh

if [ "$mode" = "deploy" ]; then
  echo "==> Streaming the chart to $chart_dir (no scp on exe.dev - tar over the exec channel)"
  tar -cz -C deploy/helm bloom \
    | "${ssh_cmd[@]}" "rm -rf '$chart_dir' && mkdir -p '$chart_dir' && tar -xz -C '$chart_dir'"
fi

echo "==> helm ${mode} of release '$release' on $DEPLOY_HOST"
"${ssh_cmd[@]}" \
  "DIR='$chart_dir' MODE='$mode' TAG='${BLOOM_IMAGE_TAG:-}' NAMESPACE='$namespace' RELEASE='$release' TIMEOUT='$timeout' REVISION='${HELM_REVISION:-}' HEALTH_HOST='$health_host' ADMIN_ORIGIN='$admin_origin' bash -s" <<'EOF'
set -euo pipefail
# Non-interactive SSH sessions skip ~/.bashrc, so default KUBECONFIG the way login shells do.
export KUBECONFIG="${KUBECONFIG:-$HOME/.kube/config}"
export PATH="$PATH:/usr/local/bin"

if [ "$MODE" = "deploy" ]; then
  # Override the committed admin-origin default with the value resolved from Cloudflare at deploy
  # time (#535); an empty ADMIN_ORIGIN means the lookup fell back, so keep the values-dev.yaml one.
  # The dashboard SPA ships to Cloudflare Pages, not this chart - the chart carries no web workload.
  extra_set=()
  if [ -n "${ADMIN_ORIGIN:-}" ]; then
    extra_set+=(--set-string "api.config.BLOOM_ADMIN_DASHBOARD_URL=$ADMIN_ORIGIN")
  fi
  helm upgrade --install "$RELEASE" "$DIR/bloom" \
    --namespace "$NAMESPACE" \
    -f "$DIR/bloom/values-dev.yaml" \
    --set image.tag="$TAG" \
    ${extra_set[@]+"${extra_set[@]}"} \
    --wait --timeout "$TIMEOUT" \
    || failed=1
else
  # No revision = the previous one, the M15 "undo the last deploy" semantics.
  helm rollback "$RELEASE" ${REVISION:+"$REVISION"} \
    --namespace "$NAMESPACE" \
    --wait --timeout "$TIMEOUT" \
    || failed=1
fi

if [ "${failed:-0}" = "1" ]; then
  echo "::error::helm $MODE failed (--wait: pods never became Ready); diagnostics:" >&2
  kubectl -n "$NAMESPACE" get pods -o wide >&2 || true
  kubectl -n "$NAMESPACE" logs "deploy/${RELEASE}-api" --tail=80 >&2 || true
  helm history "$RELEASE" --namespace "$NAMESPACE" --max 5 >&2 || true
  exit 1
fi

# Endpoint gate: --wait proved the probes; this proves end-to-end routing by hitting the API's
# real GET /health through Traefik (hostPort 80), routed by the dev overlay's ingress host -
# the same request the exe.dev edge forwards after the cutover.
echo "==> Waiting for /health through Traefik (Host: $HEALTH_HOST)"
for _ in $(seq 1 20); do
  if curl -fsS -H "Host: $HEALTH_HOST" http://127.0.0.1:80/health >/dev/null; then
    echo "Healthy. Release revisions (the deployed one is last):"
    helm history "$RELEASE" --namespace "$NAMESPACE" --max 5
    exit 0
  fi
  sleep 3
done
echo "::error::/health never came up through the ingress; diagnostics:" >&2
kubectl -n "$NAMESPACE" get pods,ingress -o wide >&2 || true
kubectl -n "$NAMESPACE" logs "deploy/${RELEASE}-api" --tail=80 >&2 || true
exit 1
EOF
