#!/usr/bin/env bash
# Nightwatch Phase 0.5 — nightly REPORT-ONLY chain (Studio, launchd ~03:00).
# detector(codex_audit) -> ingest -> verify -> report -> deliver. No repo writes, no Linear, no PRs.
set -uo pipefail
export PATH="/opt/homebrew/bin:$PATH"            # ripgrep for codex search

REPO="${NIGHTWATCH_REPO:-$HOME/CLAUDE_PROJECTS}"
CLONE="${NIGHTWATCH_CLONE:-$HOME/Code/recoil-codex}"
TOOLS="$REPO/recoil/pipeline/tools"
CODEX_AUDIT_SH="${NIGHTWATCH_AUDIT_SH:-$TOOLS/codex_audit.sh}"     # test -> stub script
NIGHTWATCH_PY="${NIGHTWATCH_PY:-python3 -m recoil.pipeline.tools.nightwatch}"  # test -> stub
SUBSYSTEMS="${NIGHTWATCH_SUBSYSTEMS:-recoil/pipeline/tools}"   # space-separated; start with 1 real dir, widen later
LOGDIR="$HOME/.recoil/nightwatch"; mkdir -p "$LOGDIR"
STAMP="$(date -u +%Y%m%d-%H%M%S)"
exec >>"$LOGDIR/nightwatch-$STAMP.log" 2>&1
TOPIC_FILE="${NTFY_TOPIC_FILE:-$HOME/.recoil/ntfy_topic}"
DF_BIN="${NIGHTWATCH_DF_BIN:-df}"
CURL_BIN="${NIGHTWATCH_CURL_BIN:-curl}"
NIGHTWATCH_MIN_FREE_KB="${NIGHTWATCH_MIN_FREE_KB:-5242880}"
FREE_KB="$("$DF_BIN" -Pk "$HOME" 2>/dev/null | awk 'NR==2 {print $4; exit}')"
case "$FREE_KB" in
  ''|*[!0-9]*)
    echo "[nightwatch] WARN could not parse df output - skipping disk guard"
    ;;
  *)
    if [ "$FREE_KB" -lt "$NIGHTWATCH_MIN_FREE_KB" ]; then
      # ntfy push DEPRECATED (REC-229) — signal degrades to the nightwatch log +
      # the exit 1 (durable, pull-readable). FOLLOW-UP: re-home this onto a
      # pull/notify surface (tracked in _v1_grounding.md / Recoil Linear).
      echo "[nightwatch] FATAL low disk: ${FREE_KB}KB free < ${NIGHTWATCH_MIN_FREE_KB}KB - aborting"
      exit 1
    fi
    ;;
esac
REPORT_OUT="$REPO/overnight-reviews/nightwatch"; mkdir -p "$REPORT_OUT"
REPORT="$REPORT_OUT/report-$STAMP.md"
AUDIT_OUT="${NIGHTWATCH_AUDIT_OUT:-$HOME/CLAUDE_PROJECTS/overnight-reviews/audit}"  # MUST match codex_audit.sh hardcoded REPORTDIR; tests override via NIGHTWATCH_AUDIT_OUT
DEADLINE=$(( $(date +%s) + 90*60 ))
TIMEOUT_BIN="$(command -v gtimeout || command -v timeout || true)"
echo "[nightwatch] start $STAMP host=$(scutil --get LocalHostName 2>/dev/null || hostname -s)"

# 0. No dispatch-active defer: Nightwatch is isolated in its own clone, writes only
# gitignored review outputs plus the ledger/report, and never pushes origin/main.
# Concurrent /dispatch builds use separate session worktrees, so there is no writer collision.

# 1. isolated clone (self-heal), fresh origin/main
if [ ! -d "$CLONE/.git" ]; then
  ORIGIN="$(git -C "$REPO" remote get-url origin)"
  echo "[nightwatch] cloning $ORIGIN -> $CLONE"
  mkdir -p "$(dirname "$CLONE")"
  git clone "$ORIGIN" "$CLONE" || { echo "[nightwatch] FATAL clone"; exit 1; }
fi
git -C "$CLONE" fetch origin --quiet && git -C "$CLONE" reset --hard origin/main --quiet \
  || { echo "[nightwatch] FATAL clone refresh"; exit 1; }

# 2. DETECTOR — codex_audit per subsystem (read-only, stdin closed), into AUDIT_OUT
mkdir -p "$AUDIT_OUT"
RUN_MARKER="$LOGDIR/audit-start-$STAMP.marker"; : > "$RUN_MARKER"
for s in $SUBSYSTEMS; do
  [ "$(date +%s)" -ge "$DEADLINE" ] && { echo "[nightwatch] deadline before audit $s"; break; }
  if [ -n "$TIMEOUT_BIN" ]; then
    "$TIMEOUT_BIN" 3000 bash "$CODEX_AUDIT_SH" "$s" </dev/null \
      || echo "[nightwatch] audit '$s' rc=$?"
  else
    bash "$CODEX_AUDIT_SH" "$s" </dev/null \
      || echo "[nightwatch] audit '$s' rc=$?"
  fi
done
FRESH_LIST="$(find "$AUDIT_OUT" -type f -name 'findings-*.json' -newer "$RUN_MARKER" 2>/dev/null | sort)"

# 3-5. INGEST -> VERIFY -> REPORT (run from $REPO so module import resolves)
cd "$REPO"
if [ -n "$FRESH_LIST" ]; then
  while IFS= read -r f; do
    [ -n "$f" ] || continue
    $NIGHTWATCH_PY ingest --repo-root "$CLONE" --audit-json "$f" </dev/null \
      || { echo "[nightwatch] FATAL ingest"; exit 1; }
  done <<EOF
$FRESH_LIST
EOF
  if [ "$(date +%s)" -lt "$DEADLINE" ]; then
    $NIGHTWATCH_PY verify --repo-root "$CLONE" --limit 5 --timeout 600 --enqueue-linear </dev/null \
      || echo "[nightwatch] verify rc=$? (continuing to report)"
  fi
  $NIGHTWATCH_PY report --include-diagnostics > "$REPORT" </dev/null \
    || { echo "[nightwatch] FATAL report"; exit 1; }
else
  echo "[nightwatch] no fresh findings this run — detector failed; skipping ingest of stale data"
  {
    echo "# ⚠ NIGHTWATCH DETECTOR FAILED — $STAMP"
    echo
    echo "No fresh codex_audit findings were produced this run. The section below reflects the EXISTING ledger (prior nights), NOT tonight's audit. Investigate \`$LOGDIR/nightwatch-$STAMP.log\`."
    echo
    $NIGHTWATCH_PY report --include-diagnostics
  } > "$REPORT" || { echo "[nightwatch] FATAL report"; exit 1; }
  DETECTOR_FAILED=1
fi
echo "[nightwatch] report -> $REPORT"

# 6. DELIVER — ntfy push DEPRECATED (REC-229); report is written + logged below.
if [ -n "${DETECTOR_FAILED:-}" ]; then
  echo "[nightwatch] DETECTOR FAILED $STAMP (report shows prior ledger): $REPORT"
else
  echo "[nightwatch] report ready: $(basename "$REPORT")"
fi
echo "[nightwatch] done $STAMP"
