#!/usr/bin/env bash
# Regression test for datetime-inject.sh.
#
# The hook injects a fresh <datetime> block as UserPromptSubmit additionalContext
# every turn, reporting "now" in $MAXY_ACCOUNT_TZ (the operator's graph timezone,
# stamped at spawn) or the server zone when that env is empty.
#
# Covers:
#   1. MAXY_ACCOUNT_TZ set          -> exit 0, valid JSON, additionalContext has
#      the <datetime> block, the named zone, and a UTC: line; source=graph.
#   2. MAXY_ACCOUNT_TZ empty        -> exit 0, block falls back to the server zone
#      and labels source as server-timezone.
#   3. Breadcrumb                   -> $LOG_DIR/datetime-inject.log gains a line
#      with source= and refresh=per-turn.
#   4. python3 absent               -> exit 0, empty stdout (fail-open).
#   5. node absent                  -> exit 0, empty stdout (fail-open).

set -u

HOOK="$(cd "$(dirname "$0")/.." && pwd)/datetime-inject.sh"
if [[ ! -x "$HOOK" ]]; then
  echo "FAIL: $HOOK not executable" >&2
  exit 1
fi

PASS=0
FAIL=0
STDIN='{"hook_event_name":"UserPromptSubmit","prompt":"when is the meeting","session_id":"sess-abc123"}'

# Case 1 — MAXY_ACCOUNT_TZ set: block reports that zone, source=graph.
out=$(printf '%s' "$STDIN" | MAXY_ACCOUNT_TZ="Europe/London" LOG_DIR="" bash "$HOOK" 2>/dev/null); ex=$?
if [[ "$ex" -eq 0 ]] && printf '%s' "$out" | python3 -c '
import json, sys
d = json.load(sys.stdin)
h = d["hookSpecificOutput"]
assert h["hookEventName"] == "UserPromptSubmit", "wrong hookEventName"
ctx = h["additionalContext"]
assert "<datetime>" in ctx and "</datetime>" in ctx, "no datetime block"
assert "Europe/London" in ctx, "zone not named"
assert "UTC:" in ctx, "no UTC line"
assert "account-timezone" in ctx, "source label missing for graph tz"
' 2>/dev/null; then
  echo "PASS: MAXY_ACCOUNT_TZ set -> datetime block in named zone"; PASS=$((PASS+1))
else
  echo "FAIL: graph-tz injection (exit=$ex stdout=$out)" >&2; FAIL=$((FAIL+1))
fi

# Case 2 — MAXY_ACCOUNT_TZ empty: falls back to the server zone, labels it.
out2=$(printf '%s' "$STDIN" | MAXY_ACCOUNT_TZ="" LOG_DIR="" bash "$HOOK" 2>/dev/null); ex2=$?
if [[ "$ex2" -eq 0 ]] && printf '%s' "$out2" | python3 -c '
import json, sys
ctx = json.load(sys.stdin)["hookSpecificOutput"]["additionalContext"]
assert "<datetime>" in ctx, "no datetime block"
assert "server-timezone" in ctx, "server fallback not labelled"
assert "UTC:" in ctx, "no UTC line"
' 2>/dev/null; then
  echo "PASS: empty MAXY_ACCOUNT_TZ -> server-zone fallback labelled"; PASS=$((PASS+1))
else
  echo "FAIL: server fallback (exit=$ex2 stdout=$out2)" >&2; FAIL=$((FAIL+1))
fi

# Case 2b — MAXY_ACCOUNT_TZ set but NOT a real IANA zone: the node validation
# rejects it, falls back to the server zone, and labels the source neutrally as
# "unavailable" (never falsely "unset" — the value was present, just unusable).
out2b=$(printf '%s' "$STDIN" | MAXY_ACCOUNT_TZ="Not/AZone" LOG_DIR="" bash "$HOOK" 2>/dev/null); ex2b=$?
if [[ "$ex2b" -eq 0 ]] && printf '%s' "$out2b" | python3 -c '
import json, sys
ctx = json.load(sys.stdin)["hookSpecificOutput"]["additionalContext"]
assert "<datetime>" in ctx, "no datetime block"
assert "server-timezone (account timezone unavailable)" in ctx, "invalid tz not labelled unavailable"
assert "Not/AZone" not in ctx, "rejected zone leaked into the block"
' 2>/dev/null; then
  echo "PASS: set-but-invalid MAXY_ACCOUNT_TZ -> server fallback, labelled unavailable"; PASS=$((PASS+1))
else
  echo "FAIL: invalid-tz fallback (exit=$ex2b stdout=$out2b)" >&2; FAIL=$((FAIL+1))
fi

# Case 3 — breadcrumb carries source= and refresh=per-turn.
LOGD=$(mktemp -d)
printf '%s' "$STDIN" | MAXY_ACCOUNT_TZ="Europe/London" LOG_DIR="$LOGD" bash "$HOOK" >/dev/null 2>&1
if [[ -f "$LOGD/datetime-inject.log" ]] \
   && grep -q "\[datetime-inject\]" "$LOGD/datetime-inject.log" \
   && grep -q "source=" "$LOGD/datetime-inject.log" \
   && grep -q "refresh=per-turn" "$LOGD/datetime-inject.log"; then
  echo "PASS: breadcrumb has source= and refresh=per-turn"; PASS=$((PASS+1))
else
  echo "FAIL: breadcrumb missing (log=$(cat "$LOGD/datetime-inject.log" 2>/dev/null))" >&2; FAIL=$((FAIL+1))
fi
rm -rf "$LOGD"

# Case 4 — python3 absent -> fail-open (exit 0, empty stdout).
FAKEBIN=$(mktemp -d)
for util in cat printf env bash node date; do
  src=$(command -v "$util" 2>/dev/null) && ln -sf "$src" "$FAKEBIN/$util" 2>/dev/null
done
nopy_out=$(printf '%s' "$STDIN" | PATH="$FAKEBIN" MAXY_ACCOUNT_TZ="Europe/London" bash "$HOOK" 2>/dev/null); nopy_exit=$?
if [[ "$nopy_exit" -eq 0 && -z "$nopy_out" ]]; then
  echo "PASS: python3 absent -> exit 0, empty stdout (fail-open)"; PASS=$((PASS+1))
else
  echo "FAIL: python3 absent (exit=$nopy_exit stdout=$nopy_out)" >&2; FAIL=$((FAIL+1))
fi
rm -rf "$FAKEBIN"

# Case 5 — node absent -> fail-open (exit 0, empty stdout).
FAKEBIN2=$(mktemp -d)
for util in cat printf env bash python3 date; do
  src=$(command -v "$util" 2>/dev/null) && ln -sf "$src" "$FAKEBIN2/$util" 2>/dev/null
done
nonode_out=$(printf '%s' "$STDIN" | PATH="$FAKEBIN2" MAXY_ACCOUNT_TZ="Europe/London" bash "$HOOK" 2>/dev/null); nonode_exit=$?
if [[ "$nonode_exit" -eq 0 && -z "$nonode_out" ]]; then
  echo "PASS: node absent -> exit 0, empty stdout (fail-open)"; PASS=$((PASS+1))
else
  echo "FAIL: node absent (exit=$nonode_exit stdout=$nonode_out)" >&2; FAIL=$((FAIL+1))
fi
rm -rf "$FAKEBIN2"

echo "----"
echo "PASS=$PASS FAIL=$FAIL"
[[ "$FAIL" -eq 0 ]]
