#!/usr/bin/env bash
set -euo pipefail

usage() {
  cat <<'EOF'
usage: scripts/promote-stable-runtime [--root <path>] [--promoted <sha>] \
                                      [--source-branch <branch>] [--source-pr <n>] \
                                      [--restart-method <text>] [--rollback <text>] \
                                      [--health-check-proof <text>] \
                                      [--identity-proof <text>] [--profile-proof <text>] \
                                      [--workspace-proof <text>] \
                                      [--mutation-capability-proof <text>]

Emit and validate a stable-control-runtime promotion record (#615).

This helper is READ-ONLY. It never fetches, merges, restarts, reloads, or kills
anything: promotion itself is an operator action documented in
docs/stable-runtime-promotion-runbook.md. The helper reads the current runtime
state, assembles the required record, validates it with
stable_control_runtime.assess_promotion_record(), and prints it for the operator
to act on and archive.

Defaults:
  --root      the repository root containing this script
  --promoted  HEAD of that root

Exit status is non-zero when the assembled record is incomplete, so a promotion
cannot be recorded without its proof fields.

Example:
  scripts/promote-stable-runtime \
    --source-branch feat/issue-615-runtime-mode-enforcement \
    --source-pr 770 \
    --restart-method "IDE client reconnect (/mcp)" \
    --rollback "git -C <root> merge --ff-only <previous-sha>; reconnect client" \
    --health-check-proof "gitea_assess_mcp_namespace_health: all four healthy" \
    --identity-proof "gitea_whoami per namespace" \
    --profile-proof "gitea_get_runtime_context per namespace" \
    --workspace-proof "process root == canonical root; clean" \
    --mutation-capability-proof "gitea_resolve_task_capability: allowed"
EOF
}

ROOT=""
PROMOTED=""
SOURCE_BRANCH=""
SOURCE_PR=""
RESTART_METHOD=""
ROLLBACK=""
HEALTH_PROOF=""
IDENTITY_PROOF=""
PROFILE_PROOF=""
WORKSPACE_PROOF=""
CAPABILITY_PROOF=""

while [[ $# -gt 0 ]]; do
  case "$1" in
    --root) ROOT="${2:-}"; shift 2 ;;
    --promoted) PROMOTED="${2:-}"; shift 2 ;;
    --source-branch) SOURCE_BRANCH="${2:-}"; shift 2 ;;
    --source-pr) SOURCE_PR="${2:-}"; shift 2 ;;
    --restart-method) RESTART_METHOD="${2:-}"; shift 2 ;;
    --rollback) ROLLBACK="${2:-}"; shift 2 ;;
    --health-check-proof) HEALTH_PROOF="${2:-}"; shift 2 ;;
    --identity-proof) IDENTITY_PROOF="${2:-}"; shift 2 ;;
    --profile-proof) PROFILE_PROOF="${2:-}"; shift 2 ;;
    --workspace-proof) WORKSPACE_PROOF="${2:-}"; shift 2 ;;
    --mutation-capability-proof) CAPABILITY_PROOF="${2:-}"; shift 2 ;;
    -h|--help) usage; exit 0 ;;
    *) echo "unknown argument: $1" >&2; usage; exit 2 ;;
  esac
done

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT="${ROOT:-$(cd "$SCRIPT_DIR/.." && pwd)}"

if ! git -C "$ROOT" rev-parse --show-toplevel >/dev/null 2>&1; then
  echo "error: '$ROOT' is not a git checkout; cannot read runtime SHAs" >&2
  exit 1
fi

PREVIOUS="${GITEA_MCP_PREVIOUS_RUNTIME_SHA:-}"
if [[ -z "$PREVIOUS" ]]; then
  # The runtime the operator is replacing. Best-effort: the commit master
  # pointed at before the fast-forward, recorded in the reflog.
  PREVIOUS="$(git -C "$ROOT" rev-parse 'master@{1}' 2>/dev/null || true)"
fi
PROMOTED="${PROMOTED:-$(git -C "$ROOT" rev-parse HEAD)}"
BRANCH="$(git -C "$ROOT" rev-parse --abbrev-ref HEAD)"
DIRTY="$(git -C "$ROOT" status --porcelain | wc -l | tr -d ' ')"
STAMP="$(date -u +%Y-%m-%dT%H:%M:%SZ)"

if [[ -z "$WORKSPACE_PROOF" ]]; then
  WORKSPACE_PROOF="root=$ROOT branch=$BRANCH dirty_files=$DIRTY"
fi
if [[ -z "$ROLLBACK" && -n "$PREVIOUS" ]]; then
  ROLLBACK="git -C $ROOT merge --ff-only $PREVIOUS (or checkout $PREVIOUS), then reload the runtime by the same sanctioned method and re-prove every namespace"
fi

cat <<EOF
# Stable control runtime promotion record (#615)
# Generated $STAMP by scripts/promote-stable-runtime (read-only)

previous_runtime_sha:      ${PREVIOUS:-<MISSING: record the SHA the runtime served before promotion>}
promoted_runtime_sha:      ${PROMOTED}
source_branch:             ${SOURCE_BRANCH:-<MISSING: pass --source-branch>}
source_pr:                 ${SOURCE_PR:-<MISSING: pass --source-pr>}
restart_method:            ${RESTART_METHOD:-<MISSING: pass --restart-method>}
health_check_proof:        ${HEALTH_PROOF:-<MISSING: pass --health-check-proof>}
identity_proof:            ${IDENTITY_PROOF:-<MISSING: pass --identity-proof>}
profile_proof:             ${PROFILE_PROOF:-<MISSING: pass --profile-proof>}
workspace_proof:           ${WORKSPACE_PROOF}
mutation_capability_proof: ${CAPABILITY_PROOF:-<MISSING: pass --mutation-capability-proof>}
rollback_instructions:     ${ROLLBACK:-<MISSING: pass --rollback>}
EOF

if [[ "$DIRTY" != "0" ]]; then
  {
    echo
    echo "WARNING: the stable checkout has $DIRTY dirty file(s); a dirty stable"
    echo "         runtime is itself a mutation blocker (dirty_stable_runtime_checkout)."
  } >&2
fi

PYTHON_BIN="${PYTHON_BIN:-python3}"
RECORD_JSON="$(
  ROOT="$ROOT" \
  PREVIOUS="$PREVIOUS" PROMOTED="$PROMOTED" \
  SOURCE_BRANCH="$SOURCE_BRANCH" SOURCE_PR="$SOURCE_PR" \
  RESTART_METHOD="$RESTART_METHOD" HEALTH_PROOF="$HEALTH_PROOF" \
  IDENTITY_PROOF="$IDENTITY_PROOF" PROFILE_PROOF="$PROFILE_PROOF" \
  WORKSPACE_PROOF="$WORKSPACE_PROOF" CAPABILITY_PROOF="$CAPABILITY_PROOF" \
  ROLLBACK="$ROLLBACK" \
  "$PYTHON_BIN" -c '
import json
import os

print(json.dumps({
    "previous_runtime_sha": os.environ.get("PREVIOUS", ""),
    "promoted_runtime_sha": os.environ.get("PROMOTED", ""),
    "source_branch": os.environ.get("SOURCE_BRANCH", ""),
    "source_pr": os.environ.get("SOURCE_PR", ""),
    "restart_method": os.environ.get("RESTART_METHOD", ""),
    "health_check_proof": os.environ.get("HEALTH_PROOF", ""),
    "identity_proof": os.environ.get("IDENTITY_PROOF", ""),
    "profile_proof": os.environ.get("PROFILE_PROOF", ""),
    "workspace_proof": os.environ.get("WORKSPACE_PROOF", ""),
    "mutation_capability_proof": os.environ.get("CAPABILITY_PROOF", ""),
    "rollback_instructions": os.environ.get("ROLLBACK", ""),
}))
'
)"

RECORD_JSON="$RECORD_JSON" ROOT="$ROOT" "$PYTHON_BIN" -c '
import json
import os
import sys

sys.path.insert(0, os.environ["ROOT"])
import stable_control_runtime as scr

record = json.loads(os.environ["RECORD_JSON"])
assessment = scr.assess_promotion_record(record)
print()
print("validation:", json.dumps(assessment, indent=2))
print()
if not assessment["valid"]:
    print("Promotion record is INCOMPLETE - do not archive it as a promotion.")
    sys.exit(1)
print("Promotion record is complete. Archive it on the tracking issue.")
'
