#!/usr/bin/env bash
#
# tidalDB pre-commit hook (tracked). Activate per-clone with:
#     git config core.hooksPath hooks
# or run scripts/install-hooks.sh once.
#
# Gates: Rust fmt/clippy/test (only when Rust is staged), the CODING_GUIDELINES
# source checks, the documentation consolidation guard (always), and site eslint
# (when node_modules exist).
#
# Two rules this file learned the hard way, 2026-08-16:
#
#   * Clippy runs WITHOUT `-D warnings`. Every tidal crate declares its own
#     posture in `[lints]` (`clippy::all = deny`, `unwrap_used = deny`,
#     `pedantic = warn`), and that table is the single source of truth. Adding
#     `-D warnings` here promoted ~58 deliberate pedantic warnings in the
#     integration tests to errors and made every Rust commit impossible.
#   * The Rust gates key off ALL crates, not a `tidal/` path match. An earlier
#     untracked copy of this hook tested `grep -q 'tidal/'`, which silently
#     skipped `tidal-server/`, `tidal-net/`, `tidal-stress/`, `tidalctl/`, and
#     `applications/` - the workspace was left to rot until a workspace-wide
#     `cargo test` was attempted months later.

set -uo pipefail
ROOT="$(git rev-parse --show-toplevel)"
cd "$ROOT" || exit 2

staged() { git diff --cached --name-only --diff-filter=ACM; }
rust_staged=$(staged | grep -E '\.rs$' || true)
site_staged=$(staged | grep -E '^site/.*\.(ts|tsx|js|jsx|mjs)$' || true)

# --- Rust -------------------------------------------------------------------
if [ -n "$rust_staged" ]; then
  echo "pre-commit: cargo fmt"
  cargo fmt || { echo "cargo fmt failed" >&2; exit 1; }
  # re-stage any files fmt rewrote
  echo "$rust_staged" | while IFS= read -r f; do [ -f "$f" ] && git add "$f"; done

  echo "pre-commit: cargo clippy -p tidaldb -p tidal-net -p tidal-server -p tidal-stress -p tidalctl"
  cargo clippy -p tidaldb -p tidal-net -p tidal-server -p tidal-stress -p tidalctl \
    --all-targets || exit 1

  echo "pre-commit: cargo test -p tidaldb --lib"
  cargo test -p tidaldb --lib || exit 1

  # ── CODING_GUIDELINES source checks (engine sources only) ─────────────────
  engine_src=$(echo "$rust_staged" | grep -E '^tidal/src/' || true)
  if [ -n "$engine_src" ]; then
    fail=0

    # §9 "one concern per file". CODING_GUIDELINES states the principle, not a
    # number; the 600-line figure is this hook's operational reading of it.
    #
    # A NEW file over the limit is a hard failure - nothing forced it to start
    # there. An EXISTING one only warns: seven engine modules already exceed it
    # (election.rs 1973, receiver.rs 1938, registry.rs 1885, ship.rs 1806,
    # multi_preference.rs 1779, pipeline.rs 1639, state_rebuild.rs 1479), so a
    # hard failure would block every fix that touches them - including the bug
    # fix that first ran into this check - and split-as-ransom is how a legacy
    # file stays untouched instead of getting smaller.
    added=$(git diff --cached --name-only --diff-filter=A | grep -E '^tidal/src/' || true)
    while IFS= read -r f; do
      [ -z "$f" ] && continue
      lines=$(wc -l < "$f" 2>/dev/null || echo 0)
      [ "$lines" -gt 600 ] || continue
      if echo "$added" | grep -qxF "$f"; then
        echo "  FAIL: new file $f starts at $lines lines (max 600) - split it now (CODING_GUIDELINES §9)" >&2
        fail=1
      else
        echo "  warn: $f is $lines lines (over 600) - split it when you next own this module (CODING_GUIDELINES §9)" >&2
      fi
    done <<< "$engine_src"

    # §11 no println!/eprintln! in engine sources - use tracing::.
    while IFS= read -r f; do
      [ -z "$f" ] && continue
      case "$f" in */benches/*) continue ;; esac
      hits=$(grep -n 'println!\|eprintln!' "$f" 2>/dev/null | grep -vE '^[0-9]+:[[:space:]]*//' || true)
      if [ -n "$hits" ]; then
        echo "  FAIL: println!/eprintln! in $f - use tracing:: (CODING_GUIDELINES §11)" >&2
        awk '{print "    " $0}' <<< "$hits" >&2
        fail=1
      fi
    done <<< "$engine_src"

    # §10 every unsafe block carries a // SAFETY: comment.
    while IFS= read -r f; do
      [ -z "$f" ] && continue
      hits=$(awk '
        /\/\/ SAFETY:/ { had_safety=1; next }
        /unsafe \{/    { if (!had_safety) print NR": "$0; had_safety=0; next }
                       { had_safety=0 }
      ' "$f" 2>/dev/null || true)
      if [ -n "$hits" ]; then
        echo "  FAIL: unsafe block without // SAFETY: in $f (CODING_GUIDELINES §10)" >&2
        awk '{print "    " $0}' <<< "$hits" >&2
        fail=1
      fi
    done <<< "$engine_src"

    [ "$fail" -eq 0 ] || exit 1
  fi
fi

# --- Documentation consolidation guard (always) -----------------------------
bash scripts/check-docs.sh || exit 1

# --- Marketing site ---------------------------------------------------------
if [ -n "$site_staged" ] && [ -d site/node_modules ]; then
  echo "pre-commit: eslint (site)"
  ( cd site && npx --no-install eslint . ) || exit 1
fi

exit 0
