#!/bin/bash
# merge_pr.sh <PR_NUMBER> [--no-tests]
# Pre-merge gate THEN merge — the wiring that makes the lint/cleanup automatic at
# merge time (gh pr merge bypasses git's local hooks, so this wrapper is the hook).
#
# Runs, on the PR's branch tip vs origin/main:
#   1. ruff check   (changed .py)            — the lint cleanup
#   2. py_compile   (changed .py)
#   3. pytest       (the PR's OWN changed test files only — scoped to dodge the
#                    known-red-collection false-block; skip with --no-tests)
# Merges (squash + delete branch) ONLY if all green. Any failure → abort, no merge.
#
# Usage: recoil/pipeline/tools/merge_pr.sh 121
set -uo pipefail

REPO="joeturnerlin/recoil"
PR="${1:?usage: merge_pr.sh <PR_NUMBER> [--no-tests]}"
RUN_TESTS=1; [ "${2:-}" = "--no-tests" ] && RUN_TESTS=0
ROOT="$(git rev-parse --show-toplevel)"
PY="$ROOT/recoil/.venv/bin/python"; [ -x "$PY" ] || PY="$(command -v python3)"

BR="$(gh pr view "$PR" --repo "$REPO" --json headRefName -q .headRefName)" || { echo "❌ no such PR #$PR"; exit 2; }
echo "── pre-merge gate: #$PR ($BR) ──"
git fetch -q origin "$BR" main

# Changed .py + changed test files vs origin/main (no worktree needed for ruff/compile).
mapfile_compat() { local __a="$1"; shift; eval "$__a=()"; while IFS= read -r l; do [ -n "$l" ] && eval "$__a+=(\"\$l\")"; done; }
CHANGED=(); CHANGED_TESTS=()
mapfile_compat CHANGED       < <(git diff --name-only --diff-filter=ACMR "origin/main...origin/$BR" -- '*.py')
mapfile_compat CHANGED_TESTS < <(git diff --name-only --diff-filter=ACMR "origin/main...origin/$BR" -- '*/tests/test_*.py' '*/test_*.py')

FAILED=0
TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
extract() { for f in "$@"; do mkdir -p "$TMP/$(dirname "$f")"; git show "origin/$BR:$f" > "$TMP/$f" 2>/dev/null; done; }

echo "changed .py: ${#CHANGED[@]} | changed tests: ${#CHANGED_TESTS[@]}"
if [ "${#CHANGED[@]}" -gt 0 ]; then
  extract "${CHANGED[@]}"
  ( cd "$TMP" && ruff check --config "$ROOT/pyproject.toml" "${CHANGED[@]}" ) && echo "ruff OK" || { echo "RUFF FAILED"; FAILED=1; }
  ( cd "$TMP" && "$PY" -m py_compile "${CHANGED[@]}" ) && echo "py_compile OK" || { echo "PY_COMPILE FAILED"; FAILED=1; }
else
  echo "no changed .py — skipping ruff/compile"
fi

# pytest on the PR's own tests, in a disposable worktree (real tree needed).
if [ "$RUN_TESTS" -eq 1 ] && [ "${#CHANGED_TESTS[@]}" -gt 0 ]; then
  WT="/tmp/merge-gate-$PR-$$"
  git worktree add -q --detach "$WT" "origin/$BR" 2>/dev/null
  ( cd "$WT" && PYTHONPATH="$WT" "$PY" -m pytest "${CHANGED_TESTS[@]}" -q -p no:cacheprovider ) \
    && echo "pytest OK (${#CHANGED_TESTS[@]} test file(s))" || { echo "PYTEST FAILED"; FAILED=1; }
  git worktree remove --force "$WT" 2>/dev/null
elif [ "$RUN_TESTS" -eq 1 ]; then
  echo "no changed test files — skipping pytest (the build already ran the PR's gates)"
fi

if [ "$FAILED" -ne 0 ]; then echo ""; echo "❌ PRE-MERGE GATE BLOCKED — NOT merging #$PR"; exit 1; fi
echo ""; echo "✅ gate green — merging #$PR"
gh pr merge "$PR" --repo "$REPO" --squash --delete-branch
