#!/bin/bash
# Sandboxed AC verification for act:dc80380c — worktree lane lifecycle
# hygiene: spawn-time env provisioning, preflight, and container reaping
# (grp:wt-noise-immunity, Lane E).
#
# Drives templates/mux/config/worktree-session-health.sh directly against a
# throwaway repo + worktree under mktemp with HOME overridden — never touches
# the real ~/.mux/worktrees, ~/.config/mux, or any live worktree. Docker is
# PATH-shimmed with a recording fake: npm test must never depend on a live
# docker daemon (or reap anything real).
set -uo pipefail

REPO_ROOT="$(cd "$(dirname "$0")/../../.." && pwd)"
SANDBOX=$(mktemp -d /tmp/cc-lifecycle-test.XXXXXX)
FAKE_HOME="$SANDBOX/home"
PROJ="$SANDBOX/proj"
WT="$SANDBOX/wt"
HEALTH="$REPO_ROOT/templates/mux/config/worktree-session-health.sh"
MUX_BIN="$REPO_ROOT/templates/mux/bin/mux"
CLEANUP_HOOK="$REPO_ROOT/templates/mux/config/worktree-cleanup.sh"

pass=0; fail=0
ok()  { echo "  PASS: $1"; pass=$((pass+1)); }
bad() { echo "  FAIL: $1"; fail=$((fail+1)); }
assert() { # assert <desc> <cmd...>
  local desc="$1"; shift
  if "$@" >/dev/null 2>&1; then ok "$desc"; else bad "$desc"; fi
}
run_health() { HOME="$FAKE_HOME" bash "$HEALTH" "$PROJ" "$WT" "$@" 2>&1; }

mkdir -p "$FAKE_HOME/.local/share/mux/wt-health" "$FAKE_HOME/.claude/projects"
trap 'rm -rf "$SANDBOX"' EXIT

# --- fixture main repo --------------------------------------------------------
git init -q "$PROJ"
git -C "$PROJ" config user.email t@t.t; git -C "$PROJ" config user.name t
mkdir -p "$PROJ/platform" "$PROJ/e2e" "$PROJ/.claude/plans"
echo plan > "$PROJ/.claude/plans/p.md"
printf '%s\n' 'platform/.env' 'e2e/.env.local' > "$PROJ/.gitignore"
printf '%s\n' 'DB_URL=postgres://main' 'KEY=s3cret' > "$PROJ/platform/.env"
echo 'E2E=1' > "$PROJ/e2e/.env.local"
cat > "$PROJ/.mux-worktree-provision" <<'EOF'
# lane env provisioning (comments + blanks ignored)

platform/.env
e2e/.env.local
check: test -f platform/.env
EOF
echo code > "$PROJ/file.txt"
git -C "$PROJ" add -A && git -C "$PROJ" commit -qm init
MAIN_SLUG=$(echo "$PROJ" | sed 's|[/.]|-|g')
mkdir -p "$FAKE_HOME/.claude/projects/$MAIN_SLUG"

git -C "$PROJ" worktree add -q "$WT" -b mux/lane HEAD

echo "== T1: spawn-time provisioning — declared env files land without hand-copying (AC1)"
run_health --refresh >"$SANDBOX/t1.out"
assert "platform/.env provisioned into worktree" \
  grep -qx 'KEY=s3cret' "$WT/platform/.env"
assert "e2e/.env.local provisioned into worktree" \
  grep -qx 'E2E=1' "$WT/e2e/.env.local"
assert "provisioning reported positively" \
  grep -q 'provisioned platform/.env from main' "$SANDBOX/t1.out"
assert "health output never prints env-file contents" \
  bash -c "! grep -q 's3cret' '$SANDBOX/t1.out'"
assert "health run exits 0" run_health

echo "== T2: freshness — stale-but-untouched copy refreshes; identical reports current"
run_health >"$SANDBOX/t2a.out"
assert "identical copy reports current" \
  grep -q 'platform/.env current with main' "$SANDBOX/t2a.out"
printf '%s\n' 'DB_URL=postgres://main' 'KEY=rotated' > "$PROJ/platform/.env"
run_health >"$SANDBOX/t2b.out"
assert "untouched worktree copy refreshed after main moved" \
  grep -qx 'KEY=rotated' "$WT/platform/.env"
assert "refresh reported as stale-but-untouched" \
  grep -q 'refreshed from main' "$SANDBOX/t2b.out"

echo "== T3: divergence — a locally-modified worktree copy is NEVER clobbered"
printf '%s\n' 'DB_URL=postgres://lane' 'PORT=5433' > "$WT/platform/.env"
printf '%s\n' 'DB_URL=postgres://main' 'KEY=rotated-again' > "$PROJ/platform/.env"
run_health >"$SANDBOX/t3.out"
assert "lane-local edit preserved (main's newer copy did not overwrite)" \
  grep -qx 'PORT=5433' "$WT/platform/.env"
assert "divergence reported, not silently skipped" \
  grep -q 'left alone' "$SANDBOX/t3.out"

echo "== T4: refusal — a declared file that is NOT gitignored fails loudly, no copy"
echo 'tracked/.env' >> "$PROJ/.mux-worktree-provision"
mkdir -p "$PROJ/tracked" && echo 'LEAK=1' > "$PROJ/tracked/.env"
run_health >"$SANDBOX/t4.out"
rc=$?
assert "health exits 1 on a non-gitignored declared file" test "$rc" -ne 0
assert "the non-ignored file was NOT copied" test ! -f "$WT/tracked/.env"
assert "refusal names the file and the reason" \
  grep -q 'tracked/.env is NOT gitignored' "$SANDBOX/t4.out"
# restore declaration for later tests
grep -v 'tracked/.env' "$PROJ/.mux-worktree-provision" > "$PROJ/.p.tmp" && mv "$PROJ/.p.tmp" "$PROJ/.mux-worktree-provision"

echo "== T5: missing source in main fails loudly (never a silent skip)"
echo 'platform/.env.missing' >> "$PROJ/.mux-worktree-provision"
run_health >"$SANDBOX/t5.out"
rc=$?
assert "health exits 1 on a declared-but-missing source" test "$rc" -ne 0
assert "missing source named in output" \
  grep -q 'source missing in main: platform/.env.missing' "$SANDBOX/t5.out"
grep -v '.env.missing' "$PROJ/.mux-worktree-provision" > "$PROJ/.p.tmp" && mv "$PROJ/.p.tmp" "$PROJ/.mux-worktree-provision"

echo "== T6: no declaration → provisioning is a silent no-op"
PROJ2="$SANDBOX/proj2"; WT2="$SANDBOX/wt2"
git init -q "$PROJ2"
git -C "$PROJ2" config user.email t@t.t; git -C "$PROJ2" config user.name t
mkdir -p "$PROJ2/.claude/plans"; echo plan > "$PROJ2/.claude/plans/p.md"
echo x > "$PROJ2/f.txt"; git -C "$PROJ2" add -A; git -C "$PROJ2" commit -qm init
MAIN2_SLUG=$(echo "$PROJ2" | sed 's|[/.]|-|g'); mkdir -p "$FAKE_HOME/.claude/projects/$MAIN2_SLUG"
git -C "$PROJ2" worktree add -q "$WT2" -b mux/lane2 HEAD
HOME="$FAKE_HOME" bash "$HEALTH" "$PROJ2" "$WT2" >"$SANDBOX/t6.out" 2>&1
assert "no-declaration health run exits 0" test $? -eq 0
assert "no provision lines emitted without a declaration" \
  bash -c "! grep -q 'provision' '$SANDBOX/t6.out'"

echo "== T7: preflight — green when sources + checks pass, red on failure, gates via exit code"
HOME="$FAKE_HOME" bash "$HEALTH" "$PROJ" --preflight >"$SANDBOX/t7a.out" 2>&1
assert "preflight passes on sound main environment" test $? -eq 0
assert "preflight reports each file positively" \
  grep -q 'present in main: platform/.env' "$SANDBOX/t7a.out"
assert "preflight runs declared check: lines" \
  grep -q 'check passed: test -f platform/.env' "$SANDBOX/t7a.out"
echo 'check: false' >> "$PROJ/.mux-worktree-provision"
HOME="$FAKE_HOME" bash "$HEALTH" "$PROJ" --preflight >"$SANDBOX/t7b.out" 2>&1
assert "preflight exits 1 on a failing check" test $? -ne 0
assert "failing check named" grep -q 'check FAILED' "$SANDBOX/t7b.out"
grep -v '^check: false$' "$PROJ/.mux-worktree-provision" > "$PROJ/.p.tmp" && mv "$PROJ/.p.tmp" "$PROJ/.mux-worktree-provision"
mv "$PROJ/platform/.env" "$PROJ/platform/.env.bak"
HOME="$FAKE_HOME" bash "$HEALTH" "$PROJ" --preflight >"$SANDBOX/t7c.out" 2>&1
assert "preflight exits 1 on a missing declared file" test $? -ne 0
mv "$PROJ/platform/.env.bak" "$PROJ/platform/.env"
HOME="$FAKE_HOME" bash "$HEALTH" "$PROJ2" --preflight >"$SANDBOX/t7d.out" 2>&1
assert "preflight trivially passes with no declaration" test $? -eq 0
assert "no-declaration preflight says so positively" \
  grep -q 'nothing to verify' "$SANDBOX/t7d.out"

# --- docker shim for reap tests -----------------------------------------------
# Records every invocation; serves canned container ids for EXACTLY the
# expected compose-project label and nothing else (sibling-lane survival).
SHIM_DIR="$SANDBOX/bin"; mkdir -p "$SHIM_DIR"
DOCKER_LOG="$SANDBOX/docker.log"
WT_NAME=$(basename "$WT" | tr '[:upper:]' '[:lower:]')
cat > "$SHIM_DIR/docker" <<EOF
#!/bin/bash
echo "\$@" >> "$DOCKER_LOG"
case "\$1" in
  info) exit \${FAKE_DOCKER_INFO_RC:-0} ;;
  ps)
    for a in "\$@"; do
      [[ "\$a" == "label=com.docker.compose.project=${WT_NAME}" ]] && { \
        if [[ -f "$SANDBOX/reaped" ]]; then exit 0; fi; \
        echo c0ffee1; echo c0ffee2; exit 0; }
    done
    exit 0 ;;
  rm) touch "$SANDBOX/reaped"; exit 0 ;;
esac
exit 0
EOF
chmod +x "$SHIM_DIR/docker"
run_reap() { rm -f "$SANDBOX/reaped"; : > "$DOCKER_LOG"; HOME="$FAKE_HOME" PATH="$SHIM_DIR:$PATH" bash "$HEALTH" "$PROJ" "$WT" --reap 2>&1; }

echo "== T8: reap — exact compose-label match, containers removed, loud positive report (AC2)"
run_reap >"$SANDBOX/t8.out"
assert "reap exits 0" test $? -eq 0
assert "docker queried with the EXACT compose-project label (no glob)" \
  grep -q -- "label=com.docker.compose.project=${WT_NAME}" "$DOCKER_LOG"
assert "no substring/name filter used (sibling lanes safe)" \
  bash -c "! grep -q -- '--filter name=' '$DOCKER_LOG'"
assert "matched containers removed via rm -f" \
  bash -c "grep -q '^rm -f c0ffee1 c0ffee2' '$DOCKER_LOG' || grep -q '^rm -f' '$DOCKER_LOG'"
assert "reap reported positively (containers only, volumes kept)" \
  grep -q 'volumes kept' "$SANDBOX/t8.out"
assert "no volume-destroying verbs ever issued" \
  bash -c "! grep -qE 'down|volume' '$DOCKER_LOG'"

echo "== T9: reap failure modes — docker absent / daemon down never block, never lie"
# "absent" = a PATH of core system dirs only (docker lives in /usr/local/bin
# or homebrew on macOS). If this machine has docker in a core dir, skip the
# absent case rather than touch a real daemon.
if PATH="/usr/bin:/bin" command -v docker >/dev/null 2>&1; then
  ok "docker absent: SKIPPED (docker present in core PATH on this machine)"
else
  HOME="$FAKE_HOME" PATH="/usr/bin:/bin" bash "$HEALTH" "$PROJ" "$WT" --reap >"$SANDBOX/t9a.out" 2>&1
  assert "docker absent: reap exits 0 silently" test $? -eq 0
fi
rm -f "$SANDBOX/reaped"; : > "$DOCKER_LOG"
HOME="$FAKE_HOME" PATH="$SHIM_DIR:$PATH" FAKE_DOCKER_INFO_RC=1 bash "$HEALTH" "$PROJ" "$WT" --reap >"$SANDBOX/t9b.out" 2>&1
assert "daemon down: reap exits 0 (removal never blocked)" test $? -eq 0
assert "daemon down: reported loudly as unverified, not silent" \
  grep -q 'daemon unreachable' "$SANDBOX/t9b.out"
assert "daemon down: no rm attempted" \
  bash -c "! grep -q '^rm' '$DOCKER_LOG'"

echo "== T10: both removal paths delegate to the ONE reap implementation (drift guard)"
assert "bin/mux delegates removal-time reaping to health --reap" \
  grep -q -- 'worktree-session-health.sh" "$proj_path" "$wt_path" --reap' "$MUX_BIN"
assert "bin/mux reaps on the clean-cleanup path" \
  bash -c "grep -B3 'Worktree cleaned up (no changes)' '$MUX_BIN' | grep -q worktree_reap_containers || grep -A8 'verdict\" == \"clean\"' '$MUX_BIN' | grep -q worktree_reap_containers"
assert "pane-exited hook delegates to the same --reap mode" \
  grep -q -- '--reap' "$CLEANUP_HOOK"
assert "contract doc references the preflight command" \
  grep -q 'mux worktree preflight' "$REPO_ROOT/templates/cabinet/worktree-invocation-contract.md"
assert "contract doc references the provisioning declaration" \
  grep -q '.mux-worktree-provision' "$REPO_ROOT/templates/cabinet/worktree-invocation-contract.md"

echo ""
echo "RESULT: ${pass} passed, ${fail} failed"
[[ "$fail" -eq 0 ]]
