#!/bin/bash
# pre_merge_gate.sh — REC-25. The deterministic pre-merge gate, DISTINCT from the
# commit hook (which runs ruff + py_compile only, NO tests). Runs over the full
# branch diff vs a base ref:
#   1/3 ruff check     (changed .py)
#   2/3 py_compile     (changed .py)
#   3/3 pytest WITH a minimum-test-count assertion — closes the phantom-green hole
#       where pytest collects 0 tests on a wrong path and still "passes".
#
# Exit 0 = gate GREEN (safe to merge). Any non-zero = BLOCKED.
#
# Usage: pre_merge_gate.sh [BASE_REF] [TEST_PATH ...]
#   BASE_REF   default: origin/main   (pass HEAD to diff nothing — ruff/compile skip)
#   TEST_PATH  default: the core pipeline test dirs
#   MIN_TESTS  env:     minimum collected tests required (default 1)
set -uo pipefail

REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
cd "$REPO_ROOT" || exit 2
export PYTHONPATH="$REPO_ROOT${PYTHONPATH:+:$PYTHONPATH}"

PY="$REPO_ROOT/recoil/.venv/bin/python"
[ -x "$PY" ] || PY="$(command -v python3)"

BASE="${1:-origin/main}"
if [ "$#" -gt 0 ]; then shift; fi
TEST_PATHS=("$@")
if [ "${#TEST_PATHS[@]}" -eq 0 ]; then
  TEST_PATHS=(recoil/pipeline/orchestrator/tests recoil/pipeline/tools/tests recoil/pipeline/core/tests)
fi
MIN_TESTS="${MIN_TESTS:-1}"

FAILED=0
section() { echo ""; echo "── $1 ──"; }
run_ruff() { if command -v ruff >/dev/null 2>&1; then ruff check "$@"; else "$PY" -m ruff check "$@"; fi; }

# Resolve a usable diff base. If NONE resolves (shallow clone / first commit),
# BLOCK rather than silently skip the diff gates — that silent-skip is the exact
# phantom-green class this gate exists to kill. (Codex review, PR #11.)
if ! git rev-parse --verify --quiet "$BASE" >/dev/null 2>&1; then
  if git rev-parse --verify --quiet HEAD~1 >/dev/null 2>&1; then
    echo "base '$BASE' unknown; falling back to HEAD~1"; BASE="HEAD~1"
  else
    echo "❌ PRE-MERGE GATE BLOCKED: cannot resolve a diff base ('$BASE' and HEAD~1 both unavailable — shallow clone or first commit). Refusing to go green."
    exit 2
  fi
fi

# Collect changed .py (bash 3.2: no mapfile).
CHANGED=()
while IFS= read -r f; do [ -n "$f" ] && CHANGED+=("$f"); done < <(git diff --name-only --diff-filter=ACMR "$BASE...HEAD" -- '*.py' 2>/dev/null)
echo "Changed .py vs $BASE: ${#CHANGED[@]} file(s)"

section "1/3 ruff"
if [ "${#CHANGED[@]}" -gt 0 ]; then
  if ! command -v ruff >/dev/null 2>&1 && ! "$PY" -c 'import ruff' >/dev/null 2>&1; then
    echo "RUFF NOT FOUND — required gate tool missing"; FAILED=1
  elif run_ruff "${CHANGED[@]}"; then echo "ruff OK"; else echo "RUFF FAILED"; FAILED=1; fi
else
  echo "no changed .py — skipping ruff"
fi

section "2/3 py_compile"
if [ "${#CHANGED[@]}" -gt 0 ]; then
  if "$PY" -m py_compile "${CHANGED[@]}"; then echo "py_compile OK"; else echo "PY_COMPILE FAILED"; FAILED=1; fi
else
  echo "no changed .py — skipping py_compile"
fi

section "3/3 pytest (require >= ${MIN_TESTS} collected)"
COLLECTED="$("$PY" -m pytest "${TEST_PATHS[@]}" --co -q -p no:cacheprovider 2>/dev/null | grep -cE '::' || true)"
echo "collected: ${COLLECTED} test(s) from: ${TEST_PATHS[*]}"
if [ "${COLLECTED:-0}" -lt "$MIN_TESTS" ]; then
  echo "MIN-TEST-COUNT FAILED: collected ${COLLECTED:-0} < required ${MIN_TESTS} (phantom-green guard tripped)"
  FAILED=1
elif "$PY" -m pytest "${TEST_PATHS[@]}" -q -p no:cacheprovider; then
  echo "pytest OK"
else
  echo "PYTEST FAILED"; FAILED=1
fi

section "4/4 engine-topology drift"
TOPO="$REPO_ROOT/recoil/architecture/topology/tools/build_topology.py"
if [ -f "$TOPO" ]; then
  if "$PY" "$TOPO" --check; then echo "topology OK"; else echo "TOPOLOGY DRIFT/CHECK FAILED"; FAILED=1; fi
else
  echo "no topology tool — skipping"
fi

echo ""
if [ "$FAILED" -eq 0 ]; then echo "✅ PRE-MERGE GATE GREEN"; exit 0; else echo "❌ PRE-MERGE GATE BLOCKED"; exit 1; fi
