#!/bin/sh
# In-Obsidian "Claude is testing" banner + countdown, so the user gets a loud,
# in-app heads-up even when they're not watching the Claude chat.
#
# Why: automated Obsidian looks identical to a normal one. If the user pokes the
# UI mid-test, their clicks collide with the automation. The chat 🔴/✅ handshake
# only helps if they're looking at the chat — this pushes the warning into
# Obsidian itself, with a grace period to back off.
#
# Usage:
#   scripts/obs-test-guard start [seconds]   # show banner, wait N s (default 10), then "testing now"
#   scripts/obs-test-guard ensure            # INSTANT banner (no wait) — re-raise it before any
#                                            #   driving eval if you're unsure it's still up
#   scripts/obs-test-guard end               # clear banner, show transient "safe to use" notice
#   scripts/obs-test-guard note 'message'    # ad-hoc persistent banner with a custom message
#
# PROTOCOL: run `start` ONCE at the beginning of a testing session and `end`
# ONLY when you're completely done and handing control back. Keep the banner up
# across EVERY intervening eval/screenshot/reload — do NOT `end` then keep
# testing. If you already ended (or aren't sure), call `ensure` (instant, no
# countdown — the user was already warned) before the next driving eval.
#
# The banner Notice is persistent (duration 0) and tracked on
# window.__claudeTestBanner so `end` can dismiss it. `start` does the wait in the
# SHELL (not inside eval — obs eval has a ~1s ceiling).

DIR="$(cd "$(dirname "$0")" && pwd)"
OBS="$DIR/obs"

show() {
  # show <js-string-literal-message> — (re)uses the single tracked banner Notice.
  "$OBS" eval code="(() => {
    const msg = $1;
    if (window.__claudeTestBanner && typeof window.__claudeTestBanner.setMessage === 'function') {
      window.__claudeTestBanner.setMessage(msg);
    } else {
      window.__claudeTestBanner = new Notice(msg, 0);
    }
    return 'shown';
  })()" >/dev/null 2>&1
}

# verify — confirm the CLI is talking to the THROWAWAY dev vault before any
# driving/destructive test, so a misrouted CLI (wrong window focused, multiple
# vaults open) can never run against a real vault. Two independent checks:
#   1. vault name === "Claude Dev Vault"
#   2. the sentinel note exists AND carries its unique token
# The sentinel makes this robust even if the vault is renamed again — a real
# vault would have to deliberately contain the token to be mistaken for this one.
# Prints "verify: OK" + exits 0 on match; LOUD error + exits 1 otherwise.
EXPECT_VAULT="Claude Dev Vault"
SENTINEL_TOKEN="claude-dev-vault-7f3a9c2e-do-not-delete"
verify() {
  out="$("$OBS" eval code="(async () => {
    const name = app.vault.getName();
    let token = '';
    try { token = await app.vault.adapter.read('CLAUDE-DEV-VAULT-SENTINEL.md'); } catch (e) {}
    const ok = name === '$EXPECT_VAULT' && token.indexOf('$SENTINEL_TOKEN') !== -1;
    return JSON.stringify({ ok, name, sentinel: token.indexOf('$SENTINEL_TOKEN') !== -1 });
  })()" 2>/dev/null)"
  case "$out" in
    *'"ok":true'*) echo "verify: OK — $EXPECT_VAULT (name + sentinel confirmed)"; return 0 ;;
    *)
      echo "✋ GUARD ABORT: the running Obsidian is NOT the throwaway dev vault." >&2
      echo "   Expected \"$EXPECT_VAULT\" + sentinel note; got: ${out:-<no response from CLI>}" >&2
      echo "   Refusing to drive — focus the Claude Dev Vault window (or restore the sentinel) and retry." >&2
      return 1 ;;
  esac
}

case "$1" in
  verify)
    verify; exit $?
    ;;
  start)
    verify || exit 1
    secs="${2:-5}"
    show "'🤖 Claude is about to run an automated Stashpad test.\n✋ Please STOP interacting — starting in about ${secs} seconds…'"
    i="$secs"
    while [ "$i" -gt 0 ]; do
      sleep 1
      i=$((i - 1))
      show "'🤖 Claude is testing Stashpad.\n✋ Please don\\'t touch — starting in ${i}s…'"
    done
    show "'🤖 Claude is TESTING Stashpad now.\n✋ Please don\\'t touch until you see the all-clear.'"
    echo "guard: banner up, ${secs}s grace elapsed — safe to drive"
    ;;
  ensure)
    # Instant re-raise (no countdown) — for follow-up driving evals when the
    # banner may have been ended. The user was already warned by the session's
    # initial `start`, so no grace period is needed. Still re-verifies the vault
    # (the user could have switched windows between evals).
    verify || exit 1
    show "'🤖 Claude is TESTING Stashpad now.\n✋ Please don\\'t touch until you see the all-clear.'"
    echo "guard: banner ensured (instant)"
    ;;
  end)
    "$OBS" eval code="(() => {
      if (window.__claudeTestBanner && typeof window.__claudeTestBanner.hide === 'function') {
        try { window.__claudeTestBanner.hide(); } catch (e) {}
      }
      window.__claudeTestBanner = null;
      new Notice('✅ Safe to use Stashpad again — Claude has finished testing.', 0);
      return 'cleared';
    })()" >/dev/null 2>&1
    echo "guard: banner cleared, all-clear shown"
    ;;
  note)
    [ -n "$2" ] || { echo "usage: obs-test-guard note 'message'" >&2; exit 1; }
    # Escape single quotes in the user message for the JS string literal.
    esc="$(printf '%s' "$2" | sed "s/'/\\\\'/g")"
    show "'$esc'"
    echo "guard: custom banner shown"
    ;;
  *)
    echo "usage: obs-test-guard {verify|start [seconds]|ensure|end|note 'message'}" >&2
    exit 1
    ;;
esac
