#!/usr/bin/env bash
# Apply and verify Overdeck GPT Actions Operator v1.
# Baseline-aware; creates an Overdeck worktree automatically when given main.
set -Eeuo pipefail

BUNDLE_NAME="overdeck-gpt-actions-operator-files-v1.zip"
BUNDLE_SHA256="3da9d099d36aeb72fca087798aec1c17754637404d486b49bd0d802b49674041"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
BUNDLE="$SCRIPT_DIR/$BUNDLE_NAME"
INPUT_REPO="${1:-$PWD}"
TMP="$(mktemp -d)"
MUTATED=0
TARGET=""
BACKUP="$TMP/backup.tar"
EXISTING_LIST="$TMP/existing.list"
ABSENT_LIST="$TMP/absent.list"
CREATED_DIRS="$TMP/created-dirs.list"

cleanup() { rm -rf "$TMP"; }
rollback() {
  local rc=$?
  if [[ "$MUTATED" == 1 && -n "$TARGET" ]]; then
    echo "[operator-apply] verification failed; restoring pre-apply worktree state" >&2
    while IFS= read -r p; do [[ -n "$p" ]] && rm -f -- "$TARGET/$p"; done < "$TMP/targets.list"
    if [[ -s "$EXISTING_LIST" ]]; then tar -C "$TARGET" -xpf "$BACKUP"; fi
    if [[ -s "$CREATED_DIRS" ]]; then
      awk '{ print length($0) "\t" $0 }' "$CREATED_DIRS" | sort -rn | cut -f2- | while IFS= read -r d; do
        [[ -n "$d" ]] && rmdir -- "$TARGET/$d" 2>/dev/null || true
      done
    fi
  fi
  cleanup
  exit "$rc"
}
trap rollback ERR INT TERM
trap cleanup EXIT

[[ -f "$BUNDLE" ]] || { echo "error: missing sibling bundle: $BUNDLE" >&2; exit 2; }
printf '%s  %s\n' "$BUNDLE_SHA256" "$BUNDLE" | sha256sum -c - >/dev/null
unzip -q "$BUNDLE" -d "$TMP/pkg"
(
  cd "$TMP/pkg"
  sha256sum -c MANIFEST.sha256 >/dev/null
)

REPO="$(cd "$INPUT_REPO" && git rev-parse --show-toplevel 2>/dev/null)" || { echo "error: not inside an Overdeck git checkout: $INPUT_REPO" >&2; exit 2; }
REPO="$(cd "$REPO" && pwd -P)"
COMMON="$(git -C "$REPO" rev-parse --path-format=absolute --git-common-dir)"
MAIN="$(cd "$COMMON/.." && pwd -P)"
DEPLOY="$HOME/.local/share/overdeck/deploy"
if [[ -d "$DEPLOY" ]]; then DEPLOY="$(cd "$DEPLOY" && pwd -P)"; fi
[[ "$REPO" != "$DEPLOY" ]] || { echo "error: refusing to mutate the deploy clone; apply to a development checkout/worktree" >&2; exit 2; }

if [[ "$REPO" == "$MAIN" ]]; then
  WT_TOOL="$REPO/modules/workstation/claude/bin/od-worktree"
  [[ -x "$WT_TOOL" ]] || WT_TOOL="$HOME/.claude/bin/od-worktree"
  [[ -x "$WT_TOOL" ]] || { echo "error: od-worktree is unavailable; create a worktree and rerun this script there" >&2; exit 2; }
  SLUG="gpt-actions-operator-$(date -u +%Y%m%d%H%M%S)"
  BASE_HEAD="$(git -C "$REPO" rev-parse HEAD)"
  echo "[operator-apply] shared main detected; creating worktree $SLUG at $BASE_HEAD"
  TARGET="$(cd "$REPO" && "$WT_TOOL" add "$SLUG" "$BASE_HEAD" | tail -n 1)"
  TARGET="$(cd "$TARGET" && pwd -P)"
else
  TARGET="$REPO"
fi

# Confirm this is the Overdeck checkout shape before any mutation.
[[ -f "$TARGET/AGENTS.md" && -f "$TARGET/packaging/deploy-local.sh" && -d "$TARGET/modules/workstation/claude" ]] || {
  echo "error: target does not look like the expected Overdeck repository: $TARGET" >&2; exit 2;
}

find "$TMP/pkg/files" -type f -printf '%P\n' | LC_ALL=C sort > "$TMP/targets.list"
: > "$EXISTING_LIST"
: > "$ABSENT_LIST"
: > "$CREATED_DIRS"

baseline_hash() { awk -v p="$1" '$2 == p {print $1; exit}' "$TMP/pkg/BASELINE.sha256"; }
target_hash() { sha256sum "$TMP/pkg/files/$1" | awk '{print $1}'; }

while IFS= read -r p; do
  want="$(target_hash "$p")"
  base="$(baseline_hash "$p")"
  if [[ -e "$TARGET/$p" ]]; then
    [[ -f "$TARGET/$p" && ! -L "$TARGET/$p" ]] || { echo "error: target path is not a regular file: $p" >&2; exit 3; }
    have="$(sha256sum "$TARGET/$p" | awk '{print $1}')"
    if [[ "$have" != "$want" && ( -z "$base" || "$have" != "$base" ) ]]; then
      echo "error: conflicting pre-existing change in $p" >&2
      echo "       current=$have" >&2
      [[ -z "$base" ]] || echo "       expected-base=$base" >&2
      echo "       package=$want" >&2
      exit 3
    fi
    printf '%s\n' "$p" >> "$EXISTING_LIST"
  else
    if [[ -n "$base" ]]; then
      echo "error: baseline tracked file is unexpectedly absent: $p" >&2
      exit 3
    fi
    printf '%s\n' "$p" >> "$ABSENT_LIST"
  fi
done < "$TMP/targets.list"

# Record only package parent directories that do not exist before mutation so rollback
# can remove directories created by install -D without touching pre-existing empty dirs.
while IFS= read -r p; do
  d="$(dirname "$p")"
  while [[ "$d" != "." && "$d" != "/" ]]; do
    [[ -e "$TARGET/$d" ]] || printf '%s\n' "$d" >> "$CREATED_DIRS"
    d="$(dirname "$d")"
  done
done < "$TMP/targets.list"
if [[ -s "$CREATED_DIRS" ]]; then sort -u -o "$CREATED_DIRS" "$CREATED_DIRS"; fi

if [[ -s "$EXISTING_LIST" ]]; then
  tar -C "$TARGET" -cpf "$BACKUP" -T "$EXISTING_LIST"
else
  tar -C "$TARGET" -cpf "$BACKUP" --files-from /dev/null
fi

while IFS=$'\t' read -r mode p; do
  [[ -n "$p" ]] || continue
  install -D -m "$mode" "$TMP/pkg/files/$p" "$TARGET/$p"
done < "$TMP/pkg/MODES.tsv"
MUTATED=1

cd "$TARGET"
echo "[operator-apply] validating source and contract"
git diff --check
for f in operator/src/*.mjs operator/test/*.mjs; do node --check "$f"; done
bash -n packaging/deploy-local.sh packaging/test-deploy-local.sh packaging/install-operator.sh packaging/install-operator-tunnel.sh
python3 - <<'PY'
from pathlib import Path
import re
p=Path('operator/gpt-actions-openapi.yaml')
s=p.read_text()
ops=re.findall(r'^\s+operationId:\s*(\S+)\s*$', s, flags=re.M)
expected=['getOperatorContext','exec','readFile','searchFiles','writeFile','patchFile','startJob','getJob']
if ops != expected:
    raise SystemExit(f'OpenAPI operationIds mismatch: {ops!r}')
if 'https://YOUR-OVERDECK-OPERATOR-HOSTNAME' not in s:
    raise SystemExit('OpenAPI server placeholder missing')
print('OpenAPI contract: 8 operationIds OK')
PY
node --test operator/test/operator.test.mjs
bash packaging/test-deploy-local.sh

MUTATED=0
trap - ERR INT TERM
BRANCH="$(git branch --show-current)"
MAIN_ROOT="$(cd "$(git rev-parse --path-format=absolute --git-common-dir)/.." && pwd -P)"
printf '\n[operator-apply] VERIFIED\n'
printf 'worktree: %s\n' "$TARGET"
printf 'branch:   %s\n' "$BRANCH"
printf 'next:     bash %q land %q %q\n' "$MAIN_ROOT/.claude/scripts/ship.sh" "$BRANCH" "$TARGET"
printf 'deploy:   bash %q\n' "$MAIN_ROOT/packaging/deploy-local.sh"
