#!/usr/bin/env bash
# Behavioral self-test for dispatch_notify.sh.
set -euo pipefail

HERE="$(cd "$(dirname "$0")" && pwd)"
ROOT="$(cd "$HERE/../../.." && pwd)"
TOOL="$ROOT/pipeline/tools/dispatch_notify.sh"
STATUS="$ROOT/pipeline/tools/dispatch_status.py"

TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT

export HOME="$TMP/home"
mkdir -p "$HOME/.recoil" "$TMP/bin"
export PATH="$TMP/bin:$PATH"
export CURL_LOG="$TMP/curl.log"
export CURL_UPDATE_BODY="$TMP/update-body.json"
export CURL_SCENARIO="linear_2xx"

cat > "$TMP/bin/curl" <<'STUB'
#!/usr/bin/env bash
set -euo pipefail

out="/dev/null"
data=""
url=""
while [ "$#" -gt 0 ]; do
  case "$1" in
    -o) out="$2"; shift 2 ;;
    -w) shift 2 ;;
    -d|--data|--data-binary) data="$2"; shift 2 ;;
    -H|-m) shift 2 ;;
    -s|-S|-sS) shift ;;
    *) url="$1"; shift ;;
  esac
done

{
  printf 'URL=%s\n' "$url"
  printf 'DATA=%s\n' "$data"
} >> "${CURL_LOG:?}"

if [ "${CURL_SCENARIO:-linear_2xx}" = "linear_5xx" ]; then
  printf '{"errors":[{"message":"simulated failure"}]}\n' > "$out"
  printf '500'
  exit 0
fi

if [[ "$data" == *commentCreate* ]]; then
  printf '{"data":{"commentCreate":{"success":true}}}\n' > "$out"
  printf '200'
  exit 0
fi

if [[ "$data" == *issueUpdate* ]]; then
  printf '%s\n' "$data" > "${CURL_UPDATE_BODY:?}"
  printf '{"data":{"issueUpdate":{"success":true}}}\n' > "$out"
  printf '200'
  exit 0
fi

cat > "$out" <<'JSON'
{
  "data": {
    "issue": {
      "id": "linear-issue-id",
      "labels": {
        "nodes": [
          {"id": "keep-1", "name": "product"},
          {"id": "old-running", "name": "dispatch-running"}
        ]
      }
    },
    "issueLabels": {
      "nodes": [
        {"id": "old-running", "name": "dispatch-running"},
        {"id": "heal-1", "name": "dispatch-healing"},
        {"id": "conv-1", "name": "dispatch-converged"},
        {"id": "human-1", "name": "dispatch-needs-human"}
      ]
    }
  }
}
JSON
printf '200'
STUB
chmod +x "$TMP/bin/curl"

init_run() {
  local run_dir="$1"
  python3 "$STATUS" init \
    --run-dir "$run_dir" \
    --issue "REC-77" \
    --branch "codex/REC-77-test" \
    --worktree "$TMP/worktree" \
    --spec "$TMP/BUILD_SPEC.md" \
    --last-validated-commit "abc123" >/dev/null
}

status_field() {
  python3 - "$1/status.json" "$2" <<'PY'
import json
import sys

with open(sys.argv[1], "r", encoding="utf-8") as handle:
    data = json.load(handle)
value = data.get(sys.argv[2])
if value is True:
    print("true")
elif value is False:
    print("false")
elif value is None:
    print("")
else:
    print(value)
PY
}

assert_contains() {
  local file="$1"
  local needle="$2"
  grep -q "$needle" "$file" || {
    echo "expected $file to contain: $needle" >&2
    exit 1
  }
}

RUN_MISSING="$TMP/run-missing"
# (REC-229: the no-op send_ntfy shim no longer reads the topic file, so only the
# linear_token removal is load-bearing here — it keeps the projection half quiet.)
rm -f "$HOME/.recoil/linear_token"
init_run "$RUN_MISSING"
bash "$TOOL" notify --run-dir "$RUN_MISSING" --event STARTED --message "Started: test run"
# REC-229: the ntfy push is a no-op shim. It still writes the notify_ntfy ledger
# event (with status=deprecated) so consumers of that event are unchanged — assert
# the ledger-preservation contract POSITIVELY (not the old channel-unconfigured branch).
assert_contains "$RUN_MISSING/events.jsonl" "notify_ntfy"
assert_contains "$RUN_MISSING/events.jsonl" '"status":"deprecated"'
# and no push HTTP call was made (the stub logs every URL it sees). The host
# pattern is assembled so this test source carries no literal push-channel URL
# (the absence gate forbids that literal anywhere under tools/, tests included).
PUSH_HOST="ntfy.""sh"
if [ -f "$CURL_LOG" ] && grep -q "$PUSH_HOST" "$CURL_LOG"; then
  echo "expected NO push HTTP call, but the request log shows one" >&2
  exit 1
fi

printf 'test-topic\n' > "$HOME/.recoil/ntfy_topic"
printf 'test-token\n' > "$HOME/.recoil/linear_token"
chmod 600 "$HOME/.recoil/ntfy_topic" "$HOME/.recoil/linear_token"

RUN_5XX="$TMP/run-5xx"
init_run "$RUN_5XX"
python3 "$STATUS" transition --run-dir "$RUN_5XX" --state ATTEMPT_RUNNING >/dev/null
export CURL_SCENARIO="linear_5xx"
bash "$TOOL" notify --run-dir "$RUN_5XX" --event ATTEMPT_RUNNING --message "Running: attempt 1"
test "$(status_field "$RUN_5XX" linear_projection_dirty)" = "true"

RUN_OK="$TMP/run-ok"
init_run "$RUN_OK"
python3 "$STATUS" transition --run-dir "$RUN_OK" --state CONVERGED_PR_CREATED --commit def456 >/dev/null
export CURL_SCENARIO="linear_2xx"
rm -f "$CURL_UPDATE_BODY"
bash "$TOOL" project-dirty --run-dir "$RUN_OK"
test "$(status_field "$RUN_OK" linear_projection_dirty)" = "false"
test -f "$CURL_UPDATE_BODY"

python3 - "$CURL_UPDATE_BODY" <<'PY'
import json
import sys

dispatch_ids = {"old-running", "heal-1", "conv-1", "human-1"}
with open(sys.argv[1], "r", encoding="utf-8") as handle:
    payload = json.load(handle)
label_ids = payload["variables"]["labelIds"]
assert "keep-1" in label_ids, label_ids
assert "conv-1" in label_ids, label_ids
assert sum(1 for label_id in label_ids if label_id in dispatch_ids) == 1, label_ids
PY

echo "dispatch_notify behavioral tests passed"
