#!/bin/bash
# Universal RouteRTL Project Pre-Push Hook (RTL-P3.536)
#
# Two concerns, in order:
#   1. Tag-lock guard (RTL-P2.726) — a pushed git TAG must point at a commit
#      that carries a committed, current ip.lock. Dev (branch) pushes are
#      deliberately unguarded. This is the only standard hook that receives the
#      ref type on stdin, so it can gate refs/tags/* ONLY.
#   2. Runs the project's declared hooks.pre_push.tests before a push, so a
#      multi-minute regression gate lives on push instead of on every commit
#      (overloading pre_commit makes people reach for --no-verify). Reads config
#      from project.yml via project_manager.py --gen-hook-config and runs each
#      test through `rr sim run`. Opt-in: nothing unless hooks.pre_push.enabled.
#
# Installed via core.hooksPath (`rr workspace install-hooks`). git invokes the
# sibling `pre-push`, which delegates here in a consumer context.

# git passes the refspecs being pushed on stdin, one per line:
#   <local ref> <local sha> <remote ref> <remote sha>
# Capture them (a closed pipe must not abort the hook) so the tag-lock guard
# can see which refs are tags. (The pre_push test gate below is ref-agnostic.)
REFSPECS="$(cat 2>/dev/null || true)"

# Collect the tag names being pushed (skip deletions — all-zero local sha).
TAG_REFS=""
while read -r local_ref local_sha remote_ref remote_sha; do
    case "$remote_ref" in refs/tags/*) ;; *) continue ;; esac
    case "$local_sha" in *[!0]*) ;; *) continue ;; esac
    TAG_REFS="$TAG_REFS ${remote_ref#refs/tags/}"
done <<EOF
$REFSPECS
EOF

if [ ! -f "project.yml" ]; then
    exit 0
fi

# 1. Python interpreter (venv first, so the SDK lookup can probe site-packages).
if [ -f ".venv/bin/python3" ]; then
    PYTHON_CMD=".venv/bin/python3"
else
    PYTHON_CMD="python3"
fi

# 2. Locate SDK tools — mirrors project-pre-commit's resolution chain so a
#    pip-installed consumer isn't silently skipped (RTL-P2.346).
if [ -d "vendor/routertl/sdk" ]; then
    SDK_TOOLS="vendor/routertl/sdk"
elif [ -n "$ROUTERTL_ROOT" ]; then
    SDK_TOOLS="$ROUTERTL_ROOT/sdk"
elif [ -d "sdk/engine" ]; then
    SDK_TOOLS="sdk"
elif PIP_SDK=$($PYTHON_CMD -c 'import os, sdk; print(os.path.dirname(sdk.__file__))' 2>/dev/null) && [ -n "$PIP_SDK" ] && [ -d "$PIP_SDK/engine" ]; then
    SDK_TOOLS="$PIP_SDK"
else
    # No SDK anywhere. The tag-lock guard (RTL-P2.726) needs the SDK to run; a
    # tag push therefore goes UNGUARDED here. Warn loudly so it's never a silent
    # skip (the server-side gate is the hard backstop), unless overridden.
    if [ -n "$TAG_REFS" ] && [ -z "$TICH_GIT_OVERRIDE" ]; then
        echo "" >&2
        echo "⚠️  Tag push detected but RouteRTL SDK not found — the ip.lock" >&2
        echo "    tag-lock guard (RTL-P2.726) could NOT run for:$TAG_REFS" >&2
        echo "    pip install routertl (or export ROUTERTL_ROOT) to enable it." >&2
    fi
    # Refuse loudly if pre_push is configured (silent skip would make users
    # believe pushes were gated when they weren't); else skip.
    if grep -qE '^[[:space:]]*pre_push:' project.yml 2>/dev/null; then
        echo "" >&2
        echo "❌ RouteRTL SDK not found — cannot run hooks.pre_push tests." >&2
        echo "   pip install routertl, export ROUTERTL_ROOT, or add vendor/routertl." >&2
        if [ "$HOOK_SKIP_IF_MISSING" = "1" ]; then
            echo "   HOOK_SKIP_IF_MISSING=1 — skipping anyway." >&2
            exit 0
        fi
        exit 1
    fi
    exit 0
fi
PROJECT_MANAGER="$SDK_TOOLS/engine/project_manager.py"

# Inject SDK repo root into PYTHONPATH so project_manager + the rr CLI import.
SDK_REPO_ROOT="${SDK_TOOLS%/*}"
if [ "$SDK_REPO_ROOT" = "$SDK_TOOLS" ]; then
    SDK_REPO_ROOT="."
fi
export PYTHONPATH="$SDK_REPO_ROOT:$PYTHONPATH"

# ── Tag-lock guard (RTL-P2.726) ──
# Runs BEFORE (and independently of) the opt-in pre_push test gate: a tag is a
# reproducibility contract whether or not the project configures push tests.
# For each pushed tag, `rr release check` verifies the tagged commit carries a
# committed, current, non-stub ip.lock. Branch (dev) pushes never reach here
# (TAG_REFS is empty), so dev stays unguarded by design.
if [ -n "$TAG_REFS" ]; then
    GUARD_FAILED=false
    for tag in $TAG_REFS; do
        if ! $PYTHON_CMD -m sdk.cli.main release check --ref "$tag"; then
            GUARD_FAILED=true
        fi
    done
    if [ "$GUARD_FAILED" = true ]; then
        if [ -n "$TICH_GIT_OVERRIDE" ]; then
            echo "" >&2
            echo "⚠️  tag-lock guard failed but TICH_GIT_OVERRIDE is set — allowing push." >&2
            echo "    Override reason: $TICH_GIT_OVERRIDE" >&2
        else
            echo "" >&2
            echo "❌ Tag push refused (RTL-P2.726): a tagged commit MUST carry a" >&2
            echo "   committed, current ip.lock — see the problem(s) above." >&2
            echo "   Fix the lock and move the tag, or set" >&2
            echo "   TICH_GIT_OVERRIDE=\"<reason>\" to override (Rule-0 escape)." >&2
            echo "   To bypass entirely: git push --no-verify" >&2
            exit 1
        fi
    fi
fi

# 3. Read hook configuration.
if ! CONFIG=$($PYTHON_CMD "$PROJECT_MANAGER" project.yml --gen-hook-config); then
    echo "❌ Failed to parse project.yml. Push aborted." >&2
    exit 1
fi
eval "$CONFIG"

# 4. Opt-in gate.
if [ "$HOOK_PUSH_ENABLED" != "true" ]; then
    exit 0
fi

if [ -z "$HOOK_PUSH_TESTS" ]; then
    echo "ℹ️  hooks.pre_push.enabled but no tests configured — nothing to gate."
    exit 0
fi

# 5. Run the configured tests via the canonical entry point.
RR="$PYTHON_CMD -m sdk.cli.main"
echo "🧪 pre-push gate: running configured tests: $HOOK_PUSH_TESTS"

# Clean the sim workspace so stale artifacts can't yield a false pass.
# shellcheck disable=SC2086
$RR workspace clean --sim || true

for test in $HOOK_PUSH_TESTS; do
    SKIP_TEST=false
    for excluded in $HOOK_PUSH_EXCLUDE_TESTS; do
        if [ "$test" = "$excluded" ]; then
            echo "   ⏭️  Skipping (excluded): $test"
            SKIP_TEST=true
            break
        fi
    done
    if [ "$SKIP_TEST" = "true" ]; then
        continue
    fi

    # tests may carry dotted module names (tests.units.test_x); `rr sim run`
    # takes the bare testbench name, so strip to the last dotted segment.
    TB="${test##*.}"
    echo "   ▶ rr sim run $TB"
    # shellcheck disable=SC2086
    if ! $RR sim run "$TB"; then
        echo "❌ Test '$TB' failed. Push aborted." >&2
        echo "   To bypass: git push --no-verify" >&2
        exit 1
    fi
done

echo "✅ pre-push gate passed."
exit 0
