#!/usr/bin/env bash
# Tests for task-register-gate.mjs — both branches: block and pass.
set -euo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
GATE="$HERE/task-register-gate.mjs"
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT
export HOME="$TMP"
FAILS=0

prompt() { printf '{"type":"user","message":{"role":"user","content":"%s"}}\n' "$1"; }
tool_use() { printf '{"type":"assistant","message":{"content":[{"type":"tool_use","name":"%s","input":{}}]}}\n' "$1"; }
tool_result() { printf '{"type":"user","toolUseResult":true,"message":{"content":[{"type":"tool_result","content":"ok"}]}}\n' ; }

run() { # $1=transcript file $2=stop_hook_active
  printf '{"transcript_path":"%s","stop_hook_active":%s}' "$1" "$2" | node "$GATE"
}

check() { # $1=name $2=expected(block|pass) $3=transcript $4=stop_hook_active
  local out
  out="$(run "$3" "$4")"
  local got=pass
  [[ "$out" == *'"decision":"block"'* ]] && got=block
  if [[ "$got" == "$2" ]]; then
    echo "ok: $1"
  else
    echo "FAIL: $1 (expected $2, got $got; out=$out)"
    FAILS=$((FAILS+1))
  fi
}

t="$TMP/edit-no-task.jsonl"
{ prompt "fix the bug"; tool_use Edit; tool_result; } >"$t"
check "Edit without task blocks" block "$t" false

t="$TMP/edit-with-task.jsonl"
{ prompt "fix the bug"; tool_use TaskCreate; tool_result; tool_use Edit; tool_result; } >"$t"
check "Edit with TaskCreate passes" pass "$t" false
check "registered retry passes" pass "$t" true

t="$TMP/task-update-only.jsonl"
{ prompt "continue"; tool_use TaskUpdate; tool_result; tool_use Write; tool_result; } >"$t"
check "TaskUpdate counts as registered" pass "$t" false

t="$TMP/trivial.jsonl"
{ prompt "what branch is this"; tool_use Bash; tool_result; } >"$t"
check "single Bash answer passes" pass "$t" false

t="$TMP/heavy-bash.jsonl"
{ prompt "investigate the crash"; for _ in 1 2 3 4; do tool_use Bash; tool_result; done; } >"$t"
check "4 work calls without task blocks" block "$t" false

t="$TMP/prior-turn-work.jsonl"
{ prompt "fix the bug"; tool_use Edit; tool_result; tool_use TaskCreate; tool_result; prompt "thanks, what changed?"; tool_use Read; tool_result; } >"$t"
check "work before last prompt ignored" pass "$t" false

t="$TMP/edit-no-task-stop-active.jsonl"
{ prompt "fix it"; tool_use Agent; tool_result; } >"$t"
check "stop_hook_active cannot bypass missing registration" block "$t" true

t="$TMP/interrupt-anchor.jsonl"
{ prompt "fix the bug"; tool_use TaskCreate; tool_result; tool_use Edit; tool_result
  printf '{"type":"user","message":{"role":"user","content":[{"type":"text","text":"[Request interrupted by user]"},{"type":"text","text":"also update the docs"}]}}\n'
  tool_use Edit; tool_result; } >"$t"
check "interrupt with new ask re-anchors and blocks" block "$t" false

echo garbage | node "$GATE" >"$TMP/garbage.out" || { echo "FAIL: garbage stdin exited nonzero"; FAILS=$((FAILS+1)); }
[[ "$(cat "$TMP/garbage.out")" == *'"decision":"block"'* ]] \
  && echo "ok: garbage stdin fails closed" \
  || { echo "FAIL: garbage stdin did not block"; FAILS=$((FAILS+1)); }

check "missing transcript fails closed" block "$TMP/does-not-exist.jsonl" false

# transcript-source union: the two sibling files lag each other, a registration
# in either one is real.
t="$TMP/lagging.jsonl"
{ prompt "fix the bug"; tool_use Agent; tool_result; } >"$t"
{ prompt "fix the bug"; tool_use Agent; tool_result; tool_use TaskCreate; tool_result; } >"$t.live.jsonl"
check "registration only in .live.jsonl sibling passes" pass "$t" false
check "same, handed the .live.jsonl path" pass "$t.live.jsonl" false

t="$TMP/lagging-neither.jsonl"
{ prompt "fix the bug"; tool_use Agent; tool_result; } >"$t"
{ prompt "fix the bug"; tool_use Agent; tool_result; } >"$t.live.jsonl"
check "no registration in either sibling blocks" block "$t" false

t="$TMP/cli-register.jsonl"
{ prompt "fix the bug"
  printf '{"type":"assistant","message":{"content":[{"type":"tool_use","name":"Bash","input":{"command":"bun ~/.claude/hooks/todo-tasks.mjs start 7"}}]}}\n'
  tool_result; tool_use Edit; tool_result; } >"$t"
check "todo-tasks CLI via Bash counts as registered" pass "$t" false

t="$TMP/cli-unrelated-bash.jsonl"
{ prompt "fix the bug"
  printf '{"type":"assistant","message":{"content":[{"type":"tool_use","name":"Bash","input":{"command":"echo todo-tasks"}}]}}\n'
  tool_result; tool_use Edit; tool_result; } >"$t"
check "unrelated Bash mentioning the CLI does not count" block "$t" false

# auto-captured tasks must be touched before a stop.
run_sess() { printf '{"transcript_path":"%s","stop_hook_active":false,"session_id":"%s"}' "$1" "$2" | node "$GATE"; }
check_sess() { # name expected transcript session
  local out got=pass
  out="$(run_sess "$3" "$4")"
  [[ "$out" == *'"decision":"block"'* ]] && got=block
  if [[ "$got" == "$2" ]]; then echo "ok: $1"; else echo "FAIL: $1 (expected $2, got $got; out=$out)"; FAILS=$((FAILS+1)); fi
}
mk_task() { # session id status
  mkdir -p "$HOME/.claude/tasks/$1"
  printf '{"id":"%s","subject":"captured ask","description":"d","status":"%s","blocks":[],"blockedBy":[],"metadata":{"autoCaptured":"true"}}' \
    "$2" "$3" >"$HOME/.claude/tasks/$1/$2.json"
}

t="$TMP/registered.jsonl"
{ prompt "do the thing"; tool_use TaskCreate; tool_result; tool_use Edit; tool_result; } >"$t"

mk_task sess-pending 1 pending
check_sess "untouched auto-captured task blocks" block "$t" sess-pending
mk_task sess-inprog 1 in_progress
check_sess "in_progress auto-captured task passes" pass "$t" sess-inprog
mk_task sess-done 1 completed
check_sess "completed/dismissed auto-captured task passes" pass "$t" sess-done
mkdir -p "$HOME/.claude/tasks/sess-none"
check_sess "session with no captured tasks passes" pass "$t" sess-none

touch "$HOME/.claude/todo-gate-off"
check_sess "escape hatch releases the gate" pass "$t" sess-pending
rm -f "$HOME/.claude/todo-gate-off"
check_sess "escape hatch removal re-arms the gate" block "$t" sess-pending

if [[ $FAILS -gt 0 ]]; then echo "$FAILS failure(s)"; exit 1; fi
echo "all tests passed"
