#!/usr/bin/env bash
#
# Run the repository's cheap gates before a push reaches the forge.  CI remains
# the hard gate; this hook makes the common failure modes cheap to find.
#
# Install once per clone:
#
#     git config core.hooksPath .githooks
#
# The mutation harness is deliberately opt-in because it is slow:
#
#     AKERR_HOOK_MUTATION=1 git push
#
# Git's normal escape hatch remains available:
#
#     git push --no-verify
#
# Builds go under .git/akerr-prepush so the hook does not disturb build/.
# Override that location with AKERR_HOOK_BUILD_DIR when needed.

set -u

ZERO_SHA=0000000000000000000000000000000000000000

root=$(git rev-parse --show-toplevel) || exit 1
cd "$root" || exit 1

# A delete-only push, or an invocation with no refs on stdin, has no new code
# to validate.
has_updates=0
while read -r _local_ref local_sha _remote_ref _remote_sha; do
    if [ "$local_sha" != "$ZERO_SHA" ]; then
	has_updates=1
    fi
done
if [ "$has_updates" -eq 0 ]; then
    exit 0
fi

builddir="${AKERR_HOOK_BUILD_DIR:-$(git rev-parse --git-dir)/akerr-prepush}"
mkdir -p "$builddir" || exit 1
logfile="$builddir/last.log"

# Keep normal output short, but preserve the complete failing command output.
run() {
    if ! "$@" > "$logfile" 2>&1; then
	echo >&2
	echo "pre-push: FAILED: $*" >&2
	echo "---------------------------------------------------------------" >&2
	cat "$logfile" >&2
	echo "---------------------------------------------------------------" >&2
	echo "pre-push: push aborted. Use 'git push --no-verify' to override." >&2
	exit 1
    fi
}

if [ -f scripts/cppcheck.sh ] && command -v cppcheck > /dev/null 2>&1; then
    echo "pre-push: cppcheck"
    run bash scripts/cppcheck.sh
elif [ ! -f scripts/cppcheck.sh ]; then
    echo "pre-push: scripts/cppcheck.sh not present, skipping cppcheck"
else
    echo "pre-push: cppcheck not installed, skipping cppcheck"
fi

echo "pre-push: default build + ctest"
run cmake -S . -B "$builddir/default"
run cmake --build "$builddir/default"
run ctest --test-dir "$builddir/default" --output-on-failure

echo "pre-push: AKERR_USE_STDLIB=OFF build + ctest"
run cmake -S . -B "$builddir/stdlib-off" \
    -DAKERR_USE_STDLIB=OFF -DAKERR_THREADS=none
run cmake --build "$builddir/stdlib-off"
run ctest --test-dir "$builddir/stdlib-off" --output-on-failure

if [ "${AKERR_HOOK_MUTATION:-0}" = "1" ]; then
    if command -v python3 > /dev/null 2>&1; then
	echo "pre-push: mutation testing (this takes a while)"
	if ! python3 scripts/mutation_test.py \
		--target src/error.c \
		--threshold "${AKERR_MUTATION_THRESHOLD:-65}"; then
	    echo >&2
	    echo "pre-push: mutation score below threshold. Push aborted." >&2
	    echo "pre-push: use 'git push --no-verify' to override." >&2
	    exit 1
	fi
    else
	echo "pre-push: python3 not installed, skipping mutation testing"
    fi
fi

echo "pre-push: OK"
exit 0
