#!/usr/bin/env bash
# wip-handoff --to <machine>
# Snapshot the current branch's in-flight state (unpushed commits, staged,
# unstaged, untracked, deletions, files listed in .wip/carry) into
# refs/wip/snap/<branch>, and move the lease refs/wip/lease/<branch> to <machine>.
# Never touches your working tree, index, branch, or refs/heads on the remote.
set -euo pipefail
die(){ echo "wip-handoff: $*" >&2; exit 1; }
TO=""; while [ $# -gt 0 ]; do case "$1" in --to) TO="$2"; shift 2;; *) die "unknown arg $1";; esac; done
ME="${MACHINE_ID:-$(git config --get wip.machine || true)}"
[ -n "$ME" ] || die "set MACHINE_ID or git config wip.machine"
[ -n "$TO" ] || die "--to <machine> required"
REMOTE="${WIP_REMOTE:-origin}"
MAX_BYTES="${WIP_MAX_BYTES:-5242880}"
BR="$(git symbolic-ref --short HEAD)" || die "detached HEAD"
TOP="$(git rev-parse --show-toplevel)"; cd "$TOP"
SNAP="refs/wip/snap/$BR"; LEASE="refs/wip/lease/$BR"

# 1. intent: a handoff without "why" and "next" is just a tarball
H=.wip/HANDOFF.md
[ -f "$H" ] || die "write $H first (sections: ## Next, ## Tried and failed, ## Do not touch)"
for s in "## Next" "## Tried and failed" "## Do not touch"; do
  body="$(awk -v s="$s" 'index($0,s)==1{f=1;next} /^## /{f=0} f' "$H" | grep -v '^[[:space:]]*$' | grep -v 'TODO' || true)"
  [ -n "$body" ] || die "$H: section '$s' is empty or TODO"
done

# 2. lease: only the current owner (or nobody) may hand off
OLD="$(git ls-remote "$REMOTE" "$LEASE" | cut -f1)"
if [ -n "$OLD" ]; then
  git fetch -q "$REMOTE" "+$LEASE:refs/wip-remote/lease/$BR"
  OWNER="$(git log -1 --format=%B "refs/wip-remote/lease/$BR" | sed -n 's/^owner=//p')"
  [ "$OWNER" = "$ME" ] || die "lease for $BR is held by $OWNER, not $ME"
fi

sleep "${WIP_TEST_PAUSE:-0}"   # test hook: widen the read-then-push window (lab/race.sh)

# 3. build snapshot: I = current index, W = worktree + untracked + .wip/carry
HEADC="$(git rev-parse HEAD)"
ITREE="$(git write-tree)"
ICOMMIT="$(git commit-tree "$ITREE" -p "$HEADC" -m "wip index $BR")"
TMPIDX="$(mktemp)"; trap 'rm -f "$TMPIDX"' EXIT
cp "$(git rev-parse --git-path index)" "$TMPIDX"
export GIT_INDEX_FILE="$TMPIDX"
git add -A
git add -f "$H"
if [ -f .wip/carry ]; then
  grep -v '^\s*#' .wip/carry | grep -v '^\s*$' | while read -r p; do git add -f -- "$p"; done
fi
WTREE="$(git write-tree)"
unset GIT_INDEX_FILE

# 4. guards on what would leave this machine
CHANGED="$(git diff --name-only --no-renames "$HEADC" "$WTREE")"
bad=""
while IFS= read -r f; do
  [ -n "$f" ] || continue
  case "$f" in .env|.env.*|*/.env|*/.env.*|*.pem|*id_rsa*) bad="$bad\n  secret-looking file: $f";; esac
  sz="$(git cat-file -s "$WTREE:$f" 2>/dev/null || echo 0)"
  [ "$sz" -le "$MAX_BYTES" ] || bad="$bad\n  too large ($sz bytes): $f  (add to .gitignore or raise WIP_MAX_BYTES)"
done <<< "$CHANGED"
hits="$(git diff "$HEADC" "$WTREE" | grep '^+' | grep -nE 'gh[pousr]_[A-Za-z0-9]{30,}|github_pat_[A-Za-z0-9_]{40,}|AKIA[0-9A-Z]{16}|sk-[A-Za-z0-9_-]{20,}|-----BEGIN [A-Z ]*PRIVATE KEY' || true)"
[ -z "$hits" ] || bad="$bad\n  token-looking content in diff:\n$(echo "$hits" | cut -c1-80 | sed 's/^/    /')"
[ -z "$bad" ] || die "refusing to snapshot:$(printf "$bad")"

STASHES="$(git stash list --format=%H 2>/dev/null || true)"
NOW="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
WCOMMIT="$(printf 'wip snapshot %s\n\nbranch=%s\nfrom=%s\nto=%s\nbase=%s\nat=%s\nstashes=%s\n' \
  "$BR" "$BR" "$ME" "$TO" "$HEADC" "$NOW" "$(echo $STASHES | wc -w)" \
  | git commit-tree "$WTREE" -p "$HEADC" -p "$ICOMMIT")"
EMPTY="$(git hash-object -t tree /dev/null)"
LCOMMIT="$(printf 'lease %s\n\nowner=%s\nfrom=%s\nsnap=%s\nat=%s\n' "$BR" "$TO" "$ME" "$WCOMMIT" "$NOW" | git commit-tree "$EMPTY")"

# 5. one atomic push: snapshot + lease (compare-and-swap on the lease)
# NOTE: no "+" on the lease refspec -- a "+" silently overrides --force-with-lease.
REFS=("+$WCOMMIT:$SNAP" "$LCOMMIT:$LEASE")
i=0; for s in $STASHES; do REFS+=("+$s:refs/wip/stash/$BR/$i"); i=$((i+1)); done
git push -q --atomic --force-with-lease="$LEASE:$OLD" "$REMOTE" "${REFS[@]}" \
  || die "push rejected (someone moved the lease?)"
git config "wip.branch.$BR.handedoff" "$TO"
echo "handed off $BR -> $TO  snap=${WCOMMIT:0:10} base=${HEADC:0:10} files=$(echo "$CHANGED" | grep -c . || true) stashes=$i"
echo "stop the agent on this machine now; pushes to $BR from $ME are blocked by the pre-push hook."
