#!/usr/bin/env bash
# Deploy, roll back, or track e2e-green status for Bloom on an exe.dev VM over SSH by PULLING a
# pre-built, SHA-tagged image (build-once, deploy-many). This is the `dev` target; the VM never
# builds from source.
#
# Modes (MODE env, default "deploy"):
#   deploy         - ship the compose file + .env, then run the image tagged BLOOM_IMAGE_TAG.
#   rollback       - redeploy the previously-deployed tag (.deployed_tag_prev) - the manual "undo the
#                    last deploy". Reuses the compose + .env already on the VM.
#   rollback_green - redeploy the last tag that PASSED e2e (.last_green_tag) - the auto-rollback the
#                    e2e stage runs when the post-deploy suite fails (E2E-7). Idempotent, and a no-op
#                    if nothing green is recorded yet.
#   promote        - mark the currently-deployed tag (.deployed_tag) as the last e2e-green
#                    (.last_green_tag); the e2e stage runs this when the suite passes. No redeploy.
#
# Requires: DEPLOY_HOST, DEPLOY_SSH_KEY (all modes); REGISTRY_USER, REGISTRY_TOKEN (every mode except
# promote); BLOOM_IMAGE_TAG (deploy only). Optional: DEPLOY_USER (exedev), DEPLOY_DIR
# (/home/<user>/bloom-ai), REGISTRY (ghcr.io), MODE (deploy). Arg 1 is the rendered env file.
#
# exe.dev specifics: no scp/sftp (files stream over `cat` exec channels); the app publishes host
# port 8080 (exe.dev's edge forwards 443 -> 8080).
set -euo pipefail

mode="${MODE:-deploy}"
: "${DEPLOY_HOST:?DEPLOY_HOST not set}"
: "${DEPLOY_SSH_KEY:?DEPLOY_SSH_KEY not set}"
case "$mode" in
  deploy) : "${BLOOM_IMAGE_TAG:?BLOOM_IMAGE_TAG not set (the SHA-tagged image built by CI)}" ;;
  rollback | rollback_green | promote) ;;
  *) echo "::error::MODE must be deploy|rollback|rollback_green|promote (got '$mode')" >&2; exit 1 ;;
esac
if [ "$mode" != "promote" ]; then
  : "${REGISTRY_USER:?REGISTRY_USER not set}"
  : "${REGISTRY_TOKEN:?REGISTRY_TOKEN not set}"
fi
env_file="${1:-env.generated}"
registry="${REGISTRY:-ghcr.io}"
image_repo="${BLOOM_IMAGE:-ghcr.io/hidden-claw/bloom-ai}"

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

if [ "$mode" = "deploy" ]; then
  echo "==> Shipping compose file + .env (no source tree; the VM pulls the image)"
  # Clear old files but preserve .env, secrets/, and the deploy/rollback bookkeeping.
  "${ssh_cmd[@]}" "mkdir -p '$dir' && find '$dir' -mindepth 1 -maxdepth 1 \
    ! -name .env ! -name secrets ! -name .deployed_tag ! -name .deployed_tag_prev ! -name .last_green_tag -exec rm -rf {} +"
  "${ssh_cmd[@]}" "cat > '$dir/docker-compose.yml'" < docker-compose.yml
  "${ssh_cmd[@]}" "cat > '$dir/.env' && chmod 600 '$dir/.env'" < "$env_file"
fi

echo "==> ${mode} on $DEPLOY_HOST"
"${ssh_cmd[@]}" \
  "DIR='$dir' MODE='$mode' TAG='${BLOOM_IMAGE_TAG:-}' REGISTRY='$registry' IMAGE_REPO='$image_repo' RUSER='${REGISTRY_USER:-}' RTOKEN='${REGISTRY_TOKEN:-}' bash -s" <<'EOF'
set -euo pipefail
cd "$DIR"

# promote: record the running tag as the last e2e-green, no redeploy.
if [ "$MODE" = "promote" ]; then
  if [ -s .deployed_tag ]; then
    cp .deployed_tag .last_green_tag
    echo "Promoted $(cat .last_green_tag) to last e2e-green."
  else
    echo "::warning::no deployed tag to promote."
  fi
  exit 0
fi

# Resolve the tag to run for the rollback modes.
if [ "$MODE" = "rollback" ]; then
  if [ ! -s .deployed_tag_prev ]; then
    echo "::error::no previous tag recorded (.deployed_tag_prev); nothing to roll back to" >&2
    exit 1
  fi
  TAG="$(cat .deployed_tag_prev)"
  echo "==> Rolling back to previous deploy: $TAG"
elif [ "$MODE" = "rollback_green" ]; then
  if [ ! -s .last_green_tag ]; then
    echo "::warning::no last e2e-green tag recorded; nothing to roll back to (leaving as-is)."
    exit 0
  fi
  TAG="$(cat .last_green_tag)"
  echo "==> Rolling back to last e2e-green: $TAG"
fi
[ -n "$TAG" ] || { echo "::error::no image tag to run" >&2; exit 1; }

echo "$RTOKEN" | docker login "$REGISTRY" -u "$RUSER" --password-stdin
export BLOOM_IMAGE_TAG="$TAG"
docker compose pull
# Remove any stray container on the app's host port, then start (never build on the VM).
docker ps -q --filter "publish=8080" | xargs -r docker rm -f 2>/dev/null || true
docker compose up -d --no-build --remove-orphans

echo "==> Waiting for health"
for i in $(seq 1 20); do
  if curl -fsS http://localhost:8080/health >/dev/null; then
    # Record bookkeeping only on a healthy run: the tag we were running becomes the rollback target,
    # and TAG becomes current. This unifies deploy (C->T) and rollback (T->C).
    current=""
    [ -s .deployed_tag ] && current="$(cat .deployed_tag)"
    if [ -n "$current" ] && [ "$current" != "$TAG" ]; then
      printf '%s\n' "$current" > .deployed_tag_prev
    fi
    printf '%s\n' "$TAG" > .deployed_tag
    # Retention: keep current + previous + last-e2e-green tags (each is a rollback target that must
    # stay local), drop older tags of this repo, then reclaim dangling layers.
    keep_prev=""; [ -s .deployed_tag_prev ] && keep_prev="$(cat .deployed_tag_prev)"
    keep_green=""; [ -s .last_green_tag ] && keep_green="$(cat .last_green_tag)"
    docker images --format '{{.Repository}}:{{.Tag}}' "$IMAGE_REPO" | while read -r ref; do
      t="${ref##*:}"
      if [ "$t" != "$TAG" ] && [ "$t" != "$keep_prev" ] && [ "$t" != "$keep_green" ] && [ "$t" != "latest" ]; then
        docker rmi "$ref" >/dev/null 2>&1 || true
      fi
    done
    docker image prune -f >/dev/null 2>&1 || true
    echo "Healthy on $TAG."
    exit 0
  fi
  sleep 3
done
echo "Health check failed for $TAG; recent app logs:" >&2
docker compose logs --tail=80 bloom >&2
exit 1
EOF
