#!/usr/bin/env bash
# Tests the one-shot quarantine restore (Task 1899): a free target is moved back,
# an occupied target is skipped with both copies surviving, an already-restored
# entry reports absent, a malformed line reports unparsable, and --dry-run
# changes nothing on disk.
set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
RESTORE="$SCRIPT_DIR/../quarantine-restore.mjs"
PASS=0; FAIL=0; FAILED=()
assert_grep()   { if printf '%s' "$2" | grep -qF "$1"; then PASS=$((PASS+1)); else FAIL=$((FAIL+1)); FAILED+=("$3: expected to find [$1]"); fi; }
assert_nogrep() { if printf '%s' "$2" | grep -qF "$1"; then FAIL=$((FAIL+1)); FAILED+=("$3: expected NOT to find [$1]"); else PASS=$((PASS+1)); fi; }
assert_file()   { if [ -e "$1" ]; then PASS=$((PASS+1)); else FAIL=$((FAIL+1)); FAILED+=("$2: expected file [$1]"); fi; }
assert_nofile() { if [ -e "$1" ]; then FAIL=$((FAIL+1)); FAILED+=("$2: expected NO file [$1]"); else PASS=$((PASS+1)); fi; }
assert_eq()     { if [ "$1" = "$2" ]; then PASS=$((PASS+1)); else FAIL=$((FAIL+1)); FAILED+=("$3: expected [$2] got [$1]"); fi; }

# One accounts root with a single account whose manifest covers all four cases.
make_tree() {
  local T; T="$(mktemp -d)"
  local A="$T/acct-1"
  mkdir -p "$A/.quarantine"
  printf '{}' > "$A/account.json"

  # (a) free target: quarantined copy exists, nothing at the root.
  printf 'portal' > "$A/.quarantine/data-portal.json"
  # (b) occupied target: quarantined copy AND a live copy the platform rewrote.
  printf 'old' > "$A/.quarantine/wa-channel-bindings.json"
  printf 'live' > "$A/wa-channel-bindings.json"
  # (c) already restored by hand: manifest names a source that is gone.
  printf 'restored' > "$A/session-titles.json"

  cat > "$A/.quarantine/manifest.jsonl" <<'EOF'
{"name":"data-portal.json","originalPath":"data-portal.json","reason":"top-level","quarantinePath":".quarantine/data-portal.json","movedAt":"2026-07-21T19:16:05.216Z"}
{"name":"session-titles.json","originalPath":"session-titles.json","reason":"top-level","quarantinePath":".quarantine/session-titles.json","movedAt":"2026-07-21T19:20:00.000Z"}
not json at all
{"name":"wa-channel-bindings.json","originalPath":"wa-channel-bindings.json","reason":"top-level","quarantinePath":".quarantine/wa-channel-bindings.json","movedAt":"2026-07-21T20:45:00.000Z"}
EOF
  printf '%s' "$T"
}

# --- dry run changes nothing -------------------------------------------------
T="$(make_tree)"; A="$T/acct-1"
OUT="$(node "$RESTORE" --accounts-root "$T" --dry-run 2>&1)"
assert_grep 'result=moved' "$OUT" "dry-run reports the move it would make"
assert_grep 'dry-run=1' "$OUT" "dry-run marks its lines"
assert_nofile "$A/data-portal.json" "dry-run leaves the root untouched"
assert_file "$A/.quarantine/data-portal.json" "dry-run leaves the quarantined copy"
assert_nofile "$A/.quarantine/restored.jsonl" "dry-run writes no restored.jsonl"
rm -rf "$T"

# --- real run ----------------------------------------------------------------
T="$(make_tree)"; A="$T/acct-1"
OUT="$(node "$RESTORE" --accounts-root "$T" 2>&1)"

assert_grep 'op=restore account=acct-1 name=data-portal.json result=moved' "$OUT" "free target moved"
assert_file "$A/data-portal.json" "free target is back at the root"
assert_nofile "$A/.quarantine/data-portal.json" "free target no longer in quarantine"

assert_grep 'name=wa-channel-bindings.json result=skipped-live-copy' "$OUT" "occupied target skipped"
assert_grep 'live' "$(cat "$A/wa-channel-bindings.json")" "live copy still wins"
assert_file "$A/.quarantine/wa-channel-bindings.json" "quarantined copy survives the skip"

assert_grep 'name=session-titles.json result=absent' "$OUT" "already-restored source reports absent"
assert_grep 'restored' "$(cat "$A/session-titles.json")" "hand-restored copy untouched"

assert_grep 'line=3 result=unparsable' "$OUT" "malformed manifest line is reported"

assert_file "$A/.quarantine/manifest.jsonl" "manifest is left intact"
RESTORED="$(cat "$A/.quarantine/restored.jsonl")"
assert_grep 'data-portal.json' "$RESTORED" "restored.jsonl records the move"
assert_nogrep 'wa-channel-bindings.json' "$RESTORED" "restored.jsonl records only moves"
assert_nogrep 'session-titles.json' "$RESTORED" "restored.jsonl records only moves"
rm -rf "$T"

# --- a name quarantined twice restores the NEWEST copy ------------------------
# The sweep took wa-channel-bindings.json up to four times on one live account,
# the platform rewriting it between each. The deleted quarantineDest() took the
# unsuffixed name first and appended .1 to the next collision, so .1 IS the newer
# copy. Walking the append-only manifest oldest-first would put the stalest copy
# back, and the newer record would then find the target occupied.
T="$(mktemp -d)"; A="$T/acct-2"
mkdir -p "$A/.quarantine"
printf '{}' > "$A/account.json"
printf 'OLDER' > "$A/.quarantine/data-portal.json"
printf 'NEWER' > "$A/.quarantine/data-portal.json.1"
cat > "$A/.quarantine/manifest.jsonl" <<'EOF'
{"name":"data-portal.json","originalPath":"data-portal.json","quarantinePath":".quarantine/data-portal.json","movedAt":"2026-07-21T19:16:05.216Z"}
{"name":"data-portal.json","originalPath":"data-portal.json","quarantinePath":".quarantine/data-portal.json.1","movedAt":"2026-07-21T20:45:00.000Z"}
EOF
OUT="$(node "$RESTORE" --accounts-root "$T" 2>&1)"
assert_grep 'NEWER' "$(cat "$A/data-portal.json")" "newest quarantined copy is the one restored"
# The target was created by THIS run, not by a live platform rewrite. Calling it
# skipped-live-copy would tell the operator the platform holds a current copy.
assert_grep 'result=skipped-superseded' "$OUT" "the older copy is superseded, not live"
assert_nogrep 'result=skipped-live-copy' "$OUT" "no live copy existed on this tree"
assert_file "$A/.quarantine/data-portal.json" "the older copy is left in quarantine, never deleted"
rm -rf "$T"

# --- a record pointing outside its account is refused -------------------------
# .quarantine is in the write guard's allowed-top-level set, so an account-scoped
# agent can append to its own manifest. This script runs as the platform user
# across every account, so an escaping path would move one account's live data
# into another.
T="$(mktemp -d)"
mkdir -p "$T/acct-a/.quarantine" "$T/acct-b"
printf '{}' > "$T/acct-a/account.json"
printf '{}' > "$T/acct-b/account.json"
printf 'LIVE' > "$T/acct-b/wa-channel-bindings.json"
printf 'x' > "$T/acct-a/.quarantine/evil.json"
cat > "$T/acct-a/.quarantine/manifest.jsonl" <<'EOF'
{"name":"evil.json","originalPath":"../acct-b/planted.json","quarantinePath":".quarantine/evil.json"}
{"name":"steal","originalPath":"stolen.json","quarantinePath":"../acct-b/wa-channel-bindings.json"}
EOF
OUT="$(node "$RESTORE" --accounts-root "$T" 2>&1)"; RC=$?
assert_grep 'result=refused-outside-account' "$OUT" "escaping record is refused"
assert_grep 'refused=2' "$OUT" "both escaping records are counted"
assert_nofile "$T/acct-b/planted.json" "nothing is planted in the other account"
assert_nofile "$T/acct-a/stolen.json" "nothing is stolen from the other account"
assert_file "$T/acct-b/wa-channel-bindings.json" "the other account's live file is untouched"
assert_eq "$RC" "1" "a refused record makes the run exit non-zero"
rm -rf "$T"

# --- a failed rename reaches the summary and the exit code --------------------
# A restore that cannot complete leaves data in quarantine. The operator runs
# this once and reads the tail line, so a clean summary with a silent stderr
# line would be the same silent-skip failure this task exists to end.
T="$(mktemp -d)"; A="$T/acct-3"
mkdir -p "$A/.quarantine"
printf '{}' > "$A/account.json"
printf 'x' > "$A/.quarantine/orphan.json"
# The target's parent bucket no longer exists, so the target itself is absent
# (not a live copy) and the rename fails with ENOENT.
cat > "$A/.quarantine/manifest.jsonl" <<'EOF'
{"name":"orphan.json","originalPath":"gone-bucket/orphan.json","quarantinePath":".quarantine/orphan.json","movedAt":"2026-07-21T19:16:05.216Z"}
EOF
OUT="$(node "$RESTORE" --accounts-root "$T" 2>&1)"; RC=$?
assert_grep 'result=failed' "$OUT" "the failed rename is reported"
assert_grep 'failed=1' "$OUT" "the failure reaches the summary line"
assert_eq "$RC" "1" "a failed restore makes the run exit non-zero"
rm -rf "$T"

# --- summary -----------------------------------------------------------------
echo "PASS=$PASS FAIL=$FAIL"
if [ "$FAIL" -gt 0 ]; then printf '%s\n' "${FAILED[@]}"; exit 1; fi
