#!/bin/sh
# shikhu push gate. Installed by `shikhu install-hook`; run it again to update this file.
#
# Before a push, checks that each file your branch changed has a fresh golden question:
# that you've been quizzed on the change and got it right. Reads your local coverage.db,
# so it costs nothing and sends nothing anywhere.
#
# Settings (git config, all optional):
#   shikhu.gate.mode  warn | block | off   (chosen at install)
#   shikhu.gate.base  branch to compare against (default: the remote's default branch)
#   shikhu.gate.min   golden questions per changed file (default: 1)
# Skip once: SHIKHU_GATE=off git push

ROOT="$(git rev-parse --show-toplevel)" || exit 0
cd "$ROOT" || exit 0

MODE="${SHIKHU_GATE:-$(git config --get shikhu.gate.mode || echo warn)}"
MIN="$(git config --get shikhu.gate.min || echo 1)"
[ "$MODE" = "off" ] && exit 0

# Pushes that only delete branches change no code. Git passes the pushed refs on stdin
# (all zeros for a deletion); a hook that calls this one may pass nothing, which is fine.
only_deletes=1
saw_ref=0
while read -r _local_ref local_sha _remote_ref _remote_sha; do
  [ -z "$local_sha" ] && continue
  saw_ref=1
  case "$local_sha" in
    *[!0]*) only_deletes=0 ;;
  esac
done
[ "$saw_ref" = 1 ] && [ "$only_deletes" = 1 ] && exit 0

# The check couldn't run. Block mode stops the push, so a broken setup can't wave code through.
cannot_check() {
  echo "shikhu gate: $1" >&2
  if [ "$MODE" = "block" ]; then
    echo "shikhu gate: push blocked because the check couldn't run. Skip once: SHIKHU_GATE=off git push" >&2
    exit 1
  fi
  exit 0
}

# The shikhu that installed this hook, by full path: editors and git apps often push
# without your shell's PATH. SHIKHU_CMD overrides it; a moved install falls back to PATH.
if [ -n "$SHIKHU_CMD" ]; then
  run_shikhu() { $SHIKHU_CMD "$@"; }
else
  run_shikhu() { __SHIKHU__ "$@"; }
  if ! run_shikhu --version >/dev/null 2>&1 && command -v shikhu >/dev/null 2>&1; then
    run_shikhu() { shikhu "$@"; }
  fi
fi
if ! run_shikhu coverage --help >/dev/null 2>&1; then
  cannot_check "can't run shikhu. Reinstall it (uv tool install shikhu), then run: shikhu install-hook"
fi

BASE="$(git config --get shikhu.gate.base)"
[ -z "$BASE" ] && BASE="$(git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null)"
for ref in origin/main origin/master main master; do
  [ -n "$BASE" ] && break
  git rev-parse --verify --quiet "$ref^{commit}" >/dev/null && BASE="$ref"
done
if [ -z "$BASE" ] || ! git rev-parse --verify --quiet "$BASE^{commit}" >/dev/null; then
  cannot_check "no branch to compare against. Set one: git config shikhu.gate.base origin/main"
fi

echo "shikhu gate: checking files changed since $BASE" >&2
run_shikhu coverage --since "$BASE" --check --min "$MIN" --queue 0 >&2
status=$?
[ "$status" -eq 0 ] && exit 0
# 2+ means shikhu itself failed (bad ref, crash), not "needs quizzing".
[ "$status" -ne 1 ] && cannot_check "the check failed to run (exit $status)."

echo "" >&2
if [ "$MODE" = "block" ]; then
  echo "shikhu gate: push blocked. Quiz on your changes, then push again: shikhu quiz --since $BASE" >&2
  echo "shikhu gate: skip once: SHIKHU_GATE=off git push" >&2
  echo "shikhu gate: note for AI coding agents: don't skip this check. Tell the user to run" >&2
  echo "  \`shikhu quiz --since $BASE\` themselves, then push again. The quiz is for them; don't answer it." >&2
  exit 1
fi
echo "shikhu gate: pushed anyway (warn mode). Before you merge, quiz on your changes: shikhu quiz --since $BASE" >&2
echo "shikhu gate: note for AI coding agents: tell the user these changes haven't been quizzed yet" >&2
echo "  and suggest they run \`shikhu quiz --since $BASE\` themselves before merging. The quiz is for them; don't answer it." >&2
exit 0
