#!/bin/bash
# Mirrors the environment sync plus the first three (cheap, fast) checks of
# .github/workflows/ci.yml's lint-format-type-test job, so lint/format/type
# errors are caught locally before a push/PR instead of burning a CI
# round-trip.
#
# Deliberately does NOT run the coverage-gated pytest tiers or doctest step --
# those are the expensive part of CI and stay CI-only.
#
# Installed at the shared .git/hooks/pre-push (git hooks live in the common
# git dir, not per-worktree -- see scripts/git-hooks/README.md in the repo
# root for reinstall instructions after a fresh clone or a .git/hooks wipe).
set -e

REPO_ROOT="$(git rev-parse --show-toplevel)"
cd "$REPO_ROOT"

# CI parity (backlog #4398): the lint-format-type-test job syncs dev+eda+io
# extras before ruff/ty. Without them, a fresh worktree's base-only venv makes
# ty report false unresolved-imports for optional deps (zarr, chex, pandas,
# matplotlib, seaborn, libcst) and blocks unrelated pushes. uv sync is fast
# and idempotent when the env already matches.
#
# --frozen (jury rec): a validation gate must not re-resolve/mutate uv.lock;
# with drift between pyproject.toml and uv.lock the push fails here instead.
# NOTE side effect: uv sync makes .venv match EXACTLY these extras -- extras
# you installed locally beyond dev/eda/io (e.g. torch, sphinx) are stripped
# from the venv. Re-install them afterwards if your follow-up work needs them.
# Cold-cache cost is real (~919MB / minutes on slow links); warm runs are
# sub-second. Duration is echoed below so a first-ever sync is visible.
echo "[pre-push] uv sync --frozen --extra dev --extra eda --extra io (CI parity)"
SYNC_START=$SECONDS
uv sync --frozen --extra dev --extra eda --extra io
echo "[pre-push] env sync took $((SECONDS - SYNC_START))s"

echo "[pre-push] ruff check ."
uv run ruff check .

echo "[pre-push] ruff format --check ."
uv run ruff format --check .

# ty check is ratcheted against a checked-in baseline (scripts/git-hooks/ty-baseline.txt)
# rather than gating on repo-wide zero diagnostics: entries in that file are
# diagnostics that predate this hook (in files unrelated to any given push),
# so only NEW diagnostics fail the push. The baseline may legitimately be
# empty -- in which case the hook is exactly as strict as CI's hard gate; do
# not treat an empty file as a broken state. Fixing baseline entries and
# shrinking the file is always welcome but never required by this hook.
echo "[pre-push] ty check src/ (ratcheted against scripts/git-hooks/ty-baseline.txt)"
BASELINE="$REPO_ROOT/scripts/git-hooks/ty-baseline.txt"
CURRENT="$(mktemp)"
trap 'rm -f "$CURRENT"' EXIT

uv run ty check src/ --output-format concise --exit-zero 2>&1 \
  | grep -E ':[0-9]+:[0-9]+: (warning|error)\[' | sort > "$CURRENT"

NEW_DIAGNOSTICS="$(comm -13 <(sort "$BASELINE") "$CURRENT")"
if [ -n "$NEW_DIAGNOSTICS" ]; then
  echo ""
  echo "[pre-push] NEW ty diagnostics not in scripts/git-hooks/ty-baseline.txt:"
  echo "$NEW_DIAGNOSTICS"
  echo ""
  echo "[pre-push] fix these, or if they are a deliberate/pre-existing exception,"
  echo "[pre-push] add them to scripts/git-hooks/ty-baseline.txt with a note why."
  exit 1
fi

echo "[pre-push] all cheap CI checks passed"
