#!/usr/bin/env bash
# bin/ayf-test -- ay-framework smoke harness
set -euo pipefail

echo "ayf-test -- ay-framework smoke harness"
echo "======================================"
echo ""

FRAMEWORK_DIR="$(cd "$(dirname "$0")/.." && pwd)"
PASS=0; FAIL=0; WARN=0

check_required() {
  local f="$1"
  if [[ -f "$f" ]]; then
    echo "  PASS  $f"; PASS=$((PASS+1))
  else
    echo "  FAIL  $f  (REQUIRED)"; FAIL=$((FAIL+1))
  fi
}

check_optional() {
  local f="$1"
  if [[ -f "$f" ]]; then
    echo "  PASS  $f"; PASS=$((PASS+1))
  else
    echo "  WARN  $f  (optional)"; WARN=$((WARN+1))
  fi
}

echo "Framework source files:"
check_required "$FRAMEWORK_DIR/package.json"
check_required "$FRAMEWORK_DIR/bin/install.js"
check_required "$FRAMEWORK_DIR/bin/ayb"
check_required "$FRAMEWORK_DIR/templates/commands/go.md"
check_required "$FRAMEWORK_DIR/templates/commands/continue.md"
check_required "$FRAMEWORK_DIR/templates/commands/pause.md"
check_required "$FRAMEWORK_DIR/hooks/pre-commit-check.sh"
check_required "$FRAMEWORK_DIR/hooks/session-start.sh"
check_required "$FRAMEWORK_DIR/bin/ayf-validate-handoff.sh"
check_required "$FRAMEWORK_DIR/bin/ayf-drift-check"
check_optional "$FRAMEWORK_DIR/bin/ayf-server.py"
check_optional "$FRAMEWORK_DIR/templates/commands/self-test.md"
check_optional "$FRAMEWORK_DIR/templates/commands/review-pack.md"
check_optional "$FRAMEWORK_DIR/templates/review-pack/template.html"

echo ""
echo "Version consistency:"
PKG_VER="$(node -p "require('$FRAMEWORK_DIR/package.json').version" 2>/dev/null || echo 'unknown')"
FILE_VER="$(cat "$FRAMEWORK_DIR/VERSION" 2>/dev/null || echo 'missing')"
if [[ "$PKG_VER" == "$FILE_VER" ]]; then
  echo "  PASS  versions match: $PKG_VER"; PASS=$((PASS+1))
else
  echo "  FAIL  version mismatch: package.json=$PKG_VER VERSION=$FILE_VER"; FAIL=$((FAIL+1))
fi

echo ""
echo "HANDOFF schema validator:"
VALIDATOR="$FRAMEWORK_DIR/bin/ayf-validate-handoff.sh"
VALID_ENTRY=$'To: all\nFrom: qa\nTask: 63\nDate: 2026-07-11 18:30\nSummary: smoke\n'
BAD_ENTRY=$'From: qa\nTask: 63\nDate: 2026-07-11 18:30\nSummary: smoke\n'

if [[ -x "$VALIDATOR" ]]; then
  echo "  PASS  validator is executable"; PASS=$((PASS+1))
else
  echo "  FAIL  validator not executable: $VALIDATOR"; FAIL=$((FAIL+1))
fi

if printf '%s' "$VALID_ENTRY" | "$VALIDATOR" - >/dev/null 2>&1; then
  echo "  PASS  valid HANDOFF entry accepted"; PASS=$((PASS+1))
else
  echo "  FAIL  valid HANDOFF entry was rejected"; FAIL=$((FAIL+1))
fi

if printf '%s' "$BAD_ENTRY" | "$VALIDATOR" - >/dev/null 2>&1; then
  echo "  FAIL  entry missing 'To:' was accepted"; FAIL=$((FAIL+1))
else
  echo "  PASS  entry missing 'To:' rejected"; PASS=$((PASS+1))
fi

echo ""
echo "Plan drift detector:"
DRIFT="$FRAMEWORK_DIR/bin/ayf-drift-check"
DRIFT_TMP="$(mktemp -d)"
trap 'rm -rf "$DRIFT_TMP"' EXIT
( cd "$DRIFT_TMP" && git init -q && git commit -q --allow-empty -m init )
printf '## Files to Create\n- `%s`\n- `does/not/exist.ts`\n' "present.txt" > "$DRIFT_TMP/task.md"
: > "$DRIFT_TMP/present.txt"

if [[ -x "$DRIFT" ]]; then
  echo "  PASS  drift-check is executable"; PASS=$((PASS+1))
else
  echo "  FAIL  drift-check not executable: $DRIFT"; FAIL=$((FAIL+1))
fi

DRIFT_OUT="$( cd "$DRIFT_TMP" && "$DRIFT" task.md 2>/dev/null || true )"
DRIFT_CODE=0
( cd "$DRIFT_TMP" && "$DRIFT" task.md >/dev/null 2>&1 ) || DRIFT_CODE=$?

if grep -q $'MATCHED\tpresent.txt' <<< "$DRIFT_OUT"; then
  echo "  PASS  present file reported MATCHED"; PASS=$((PASS+1))
else
  echo "  FAIL  present file not reported MATCHED"; FAIL=$((FAIL+1))
fi

if grep -q $'MISSING\tdoes/not/exist.ts' <<< "$DRIFT_OUT"; then
  echo "  PASS  absent declared file reported MISSING"; PASS=$((PASS+1))
else
  echo "  FAIL  absent declared file not reported MISSING"; FAIL=$((FAIL+1))
fi

if [[ "$DRIFT_CODE" -ne 0 ]]; then
  echo "  PASS  exits non-zero on drift ($DRIFT_CODE)"; PASS=$((PASS+1))
else
  echo "  FAIL  did not exit non-zero on drift"; FAIL=$((FAIL+1))
fi

echo ""
echo "Install smoke (temp dir):"
# Actually run the installer into a throwaway dir and assert the OUTPUT artifacts
# exist — this is the real end-to-end check (the sections above only test repo
# source files). Covers Human/, installed hooks, and the demo HTML specifically.
INSTALL_TMP="$(mktemp -d)"
# Extend cleanup to cover both temp dirs (a bare `trap ... EXIT` would replace the
# drift-detector trap above and leak DRIFT_TMP).
trap 'rm -rf "$DRIFT_TMP" "$INSTALL_TMP"' EXIT

if ( cd "$INSTALL_TMP" && node "$FRAMEWORK_DIR/bin/install.js" --local --yes ) >"$INSTALL_TMP/install.log" 2>&1; then
  echo "  PASS  installer ran without error"; PASS=$((PASS+1))
else
  echo "  FAIL  installer exited non-zero (last 20 log lines below)"; FAIL=$((FAIL+1))
  tail -20 "$INSTALL_TMP/install.log" | sed 's/^/          /'
fi

check_installed() {
  local rel="$1"
  if [[ -e "$INSTALL_TMP/$rel" ]]; then
    echo "  PASS  installed: $rel"; PASS=$((PASS+1))
  else
    echo "  FAIL  missing after install: $rel"; FAIL=$((FAIL+1))
  fi
}
check_installed ".ay/state.json"
check_installed ".ay/tracking/BOARD.md"
check_installed ".ay/tracking/HANDOFFS.md"
check_installed ".claude/commands/go.md"
check_installed ".claude/hooks/session-start.sh"   # hooks were installed
check_installed "Human/index.html"                 # Human/ guides were copied
check_installed "lifecycle-demo.html"              # demo HTML was copied

echo ""
echo ".ay/ single-source-of-truth:"
# .ay/ must not be tracked in git (gitignored operational state)
TRACKED_AY="$(git -C "$FRAMEWORK_DIR" ls-files .ay/ 2>/dev/null)"
if [[ -z "$TRACKED_AY" ]]; then
  echo "  PASS  .ay/ is not tracked in git"; PASS=$((PASS+1))
else
  echo "  FAIL  .ay/ files are tracked in git (run: git rm --cached .ay/...)"; FAIL=$((FAIL+1))
  echo "$TRACKED_AY" | sed 's/^/          /'
fi

# .ay/ resolver must work (git common-dir approach)
AY_RESOLVED="$(dirname "$(git -C "$FRAMEWORK_DIR" rev-parse --git-common-dir 2>/dev/null)")/.ay"
if [[ -d "$AY_RESOLVED" ]]; then
  echo "  PASS  .ay/ resolver works: $AY_RESOLVED"; PASS=$((PASS+1))
else
  echo "  WARN  .ay/ resolver returned non-existent path: $AY_RESOLVED"; WARN=$((WARN+1))
fi

echo ""
echo "Context-pressure auto-pause hook:"
CP_HOOK="$FRAMEWORK_DIR/hooks/context-pressure.sh"
if [[ -x "$CP_HOOK" ]]; then
  echo "  PASS  hook is executable"; PASS=$((PASS+1))
else
  echo "  FAIL  hook missing or not executable: $CP_HOOK"; FAIL=$((FAIL+1))
fi

CP_TMP="$(mktemp -d)"
mkdir -p "$CP_TMP/.ay/tracking/locks"
printf 'task-99\nagent: qa\nsession: cptest\nphase: TEST\n' > "$CP_TMP/.ay/tracking/locks/task-99.lock"
printf '{"context_pressure_threshold":0.8}\n' > "$CP_TMP/.ay/config.json"

# Over threshold -> snapshot written + PAUSED message, task id resolved by session
CP_OUT="$(cd "$CP_TMP" && CLAUDE_CONTEXT_TOKENS=180000 CLAUDE_CONTEXT_WINDOW=200000 CLAUDE_CODE_SESSION_ID=cptest bash "$CP_HOOK" </dev/null 2>&1 || true)"
CP_SNAP="$(find "$CP_TMP/.ay/tracking" -name 'context-snapshot-*.json' 2>/dev/null | head -1)"
if [[ -n "$CP_SNAP" ]] && printf '%s' "$CP_OUT" | grep -q "PAUSED"; then
  echo "  PASS  over-threshold writes snapshot + prints PAUSED"; PASS=$((PASS+1))
else
  echo "  FAIL  over-threshold did not pause/snapshot"; FAIL=$((FAIL+1))
fi

# Snapshot content: required fields present, session-owned task id + phase resolved
if [[ -n "$CP_SNAP" ]] \
   && grep -q '"task_id": "task-99"' "$CP_SNAP" \
   && grep -q '"last_completed_phase": "TEST"' "$CP_SNAP" \
   && grep -q '"staged_files"' "$CP_SNAP" \
   && grep -q '"timestamp"' "$CP_SNAP"; then
  echo "  PASS  snapshot has task id, phase, staged files, timestamp"; PASS=$((PASS+1))
else
  echo "  FAIL  snapshot missing required fields"; FAIL=$((FAIL+1))
fi

# Below threshold -> advisory only, no snapshot
CP_TMP2="$(mktemp -d)"
mkdir -p "$CP_TMP2/.ay/tracking/locks"
printf '{"context_pressure_threshold":0.8}\n' > "$CP_TMP2/.ay/config.json"
( cd "$CP_TMP2" && CLAUDE_CONTEXT_TOKENS=120000 CLAUDE_CONTEXT_WINDOW=200000 bash "$CP_HOOK" </dev/null >/dev/null 2>&1 || true )
if find "$CP_TMP2/.ay/tracking" -name 'context-snapshot-*.json' 2>/dev/null | grep -q .; then
  echo "  FAIL  below-threshold wrote a snapshot"; FAIL=$((FAIL+1))
else
  echo "  PASS  below-threshold writes no snapshot"; PASS=$((PASS+1))
fi
rm -rf "$CP_TMP" "$CP_TMP2"

echo ""
echo "Ghost-op scan (task 88):"
GS="$FRAMEWORK_DIR/bin/ayf-ghost-scan"
if [[ -x "$GS" ]]; then
  echo "  PASS  bin/ayf-ghost-scan is executable"; PASS=$((PASS+1))
else
  echo "  FAIL  bin/ayf-ghost-scan missing or not executable"; FAIL=$((FAIL+1))
fi

GS_TMP="$(mktemp -d)"
printf 'step 1: build\nstep 2: git push origin main\n' > "$GS_TMP/bad-push.md"
printf 'git merge feature\ngh pr create --fill\ngh pr merge 3\nnpm publish\n' > "$GS_TMP/bad-many.md"
printf 'git commit -m "ready to push and merge"\nWe pushed and merged already.\ngit add bin/x\n' > "$GS_TMP/clean.md"

# Each ghost op is detected (exit 1)
if "$GS" "$GS_TMP/bad-push.md" >/dev/null 2>&1; then
  echo "  FAIL  did not flag 'git push'"; FAIL=$((FAIL+1))
else
  echo "  PASS  flags 'git push' (exit 1)"; PASS=$((PASS+1))
fi

# All four op families flagged in one file
GS_HITS="$("$GS" "$GS_TMP/bad-many.md" 2>/dev/null | wc -l | tr -d ' ' || true)"
if [[ "$GS_HITS" -eq 4 ]]; then
  echo "  PASS  flags git merge + gh pr create/merge + npm publish (4 offenders)"; PASS=$((PASS+1))
else
  echo "  FAIL  expected 4 offenders, got $GS_HITS"; FAIL=$((FAIL+1))
fi

# Quoted mentions + past-tense words do NOT false-positive (exit 0)
if "$GS" "$GS_TMP/clean.md" >/dev/null 2>&1; then
  echo "  PASS  no false-positive on quoted/past-tense mentions"; PASS=$((PASS+1))
else
  echo "  FAIL  false-positive on a clean plan"; FAIL=$((FAIL+1))
fi

# stdin mode works
if printf 'git push\n' | "$GS" - >/dev/null 2>&1; then
  echo "  FAIL  stdin mode did not flag 'git push'"; FAIL=$((FAIL+1))
else
  echo "  PASS  stdin mode flags ghost op"; PASS=$((PASS+1))
fi
rm -rf "$GS_TMP"

# Both go.md copies invoke the gate
if grep -q "ayf-ghost-scan" "$FRAMEWORK_DIR/.claude/commands/go.md" \
   && grep -q "ayf-ghost-scan" "$FRAMEWORK_DIR/templates/commands/go.md"; then
  echo "  PASS  /go Phase 4 wired in both .claude and templates go.md"; PASS=$((PASS+1))
else
  echo "  FAIL  ghost-scan not wired into both go.md copies"; FAIL=$((FAIL+1))
fi

echo ""
echo "Operator log template (task 86):"
OL="$FRAMEWORK_DIR/templates/operator-log.jsonl"
if [[ -f "$OL" ]]; then
  echo "  PASS  templates/operator-log.jsonl exists"; PASS=$((PASS+1))
else
  echo "  FAIL  templates/operator-log.jsonl missing"; FAIL=$((FAIL+1))
fi
# First line documents the schema as a comment
if head -1 "$OL" 2>/dev/null | grep -q '^#'; then
  echo "  PASS  first line is a comment schema-header"; PASS=$((PASS+1))
else
  echo "  FAIL  first line is not a '#' schema-header"; FAIL=$((FAIL+1))
fi
# Stripping comments yields valid JSONL (a fresh template has zero data lines)
OL_BAD=0
while IFS= read -r line; do
  case "$line" in ''|\#*) continue;; esac
  if command -v jq >/dev/null 2>&1; then
    printf '%s' "$line" | jq -e . >/dev/null 2>&1 || OL_BAD=1
  fi
done < "$OL"
if [[ "$OL_BAD" -eq 0 ]]; then
  echo "  PASS  data lines (comments stripped) are valid JSONL"; PASS=$((PASS+1))
else
  echo "  FAIL  a non-comment line is not valid JSON"; FAIL=$((FAIL+1))
fi
# The documented example carries the exact schema (6 fields + a valid action)
if command -v jq >/dev/null 2>&1; then
  OL_EX="$(grep '^# {' "$OL" 2>/dev/null | sed 's/^# //' | head -1)"
  if [[ -n "$OL_EX" ]] && printf '%s' "$OL_EX" | jq -e '
        has("ts") and has("action") and has("surface") and has("task_id")
        and has("subject") and has("detail")
        and (.action | IN("APPROVE","NUDGE","DENY","BLOCKED","HOLD"))' >/dev/null 2>&1; then
    echo "  PASS  documented example has 6 fields + valid action enum"; PASS=$((PASS+1))
  else
    echo "  FAIL  documented example missing fields or bad action enum"; FAIL=$((FAIL+1))
  fi
else
  echo "  WARN  jq absent — skipped operator-log JSON schema check"; WARN=$((WARN+1))
fi

echo ""
echo "======================================"
echo "Results: $PASS passed, $FAIL failed, $WARN warnings"
if [[ "$FAIL" -eq 0 ]]; then
  echo "STATUS: PASS"; exit 0
else
  echo "STATUS: FAIL"; exit 1
fi
