#!/bin/sh
#
# Reindent staged C sources to the canonical style before the commit is made.
# See AGENTS.md -> "Coding Style".
#
# Enable with:
#     git config core.hooksPath scripts/hooks
# Bypass a single commit with `git commit --no-verify`.
#
# The hook checks the *staged* content, not the working tree, so what gets
# committed is what was verified. When a file needs reindenting it is fixed in
# the working tree and re-staged -- but only when the working tree and the index
# agree for that file. If they disagree (a partial `git add -p`), re-staging
# would sweep unstaged work into the commit, so the hook stops and asks you to
# do it yourself.

set -eu

root=$(git rev-parse --show-toplevel)
reindent="$root/scripts/reindent.sh"

# Leave conflict resolution alone.
if [ -e "$root/.git/MERGE_HEAD" ]; then
    exit 0
fi

if [ ! -x "$reindent" ]; then
    echo "pre-commit: $reindent missing or not executable; skipping style check" >&2
    exit 0
fi

# Without Emacs the canonical style cannot be applied or even checked. Warn
# rather than block: a hook that fails closed on a missing optional tool just
# teaches everyone to pass --no-verify.
if ! command -v emacs >/dev/null 2>&1; then
    echo "pre-commit: emacs not found; skipping reindent (see AGENTS.md)" >&2
    exit 0
fi

# Keep this list in step with SCOPE in scripts/reindent.sh. They are two copies
# of one decision, and they have already disagreed once: `tools` and `examples`
# arrived with the documentation harness and were reindentable by hand while
# neither this hook nor `--check` could see them.
staged=$(git diff --cached --name-only --diff-filter=ACMR \
         | grep -E '^(src|include|tests|util|tools|examples)/.*\.[ch]$' \
         | grep -v -x -F 'include/akgl/SDL_GameControllerDB.h' || true)

if [ -z "$staged" ]; then
    exit 0
fi

tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT INT TERM

# Reindent a copy of each file's staged content and see whether it moves.
needs=''
for f in $staged; do
    copy="$tmp/$(printf '%s' "$f" | tr '/' '_')"
    git show ":$f" >"$copy"
    cp "$copy" "$copy.orig"
    if ! "$reindent" "$copy" >/dev/null 2>&1; then
        echo "pre-commit: reindent failed on $f; commit aborted" >&2
        exit 1
    fi
    if ! cmp -s "$copy" "$copy.orig"; then
        needs="$needs $f"
    fi
done

if [ -z "$needs" ]; then
    exit 0
fi

# Refuse to touch anything if even one file is partially staged.
blocked=''
for f in $needs; do
    if ! git diff --quiet -- "$f"; then
        blocked="$blocked $f"
    fi
done

if [ -n "$blocked" ]; then
    echo "pre-commit: these files need reindenting but have unstaged changes," >&2
    echo "so re-staging them would pull unstaged work into the commit:" >&2
    for f in $blocked; do echo "    $f" >&2; done
    echo >&2
    echo "Reindent and stage them yourself, then commit again:" >&2
    echo "    scripts/reindent.sh$(for f in $blocked; do printf ' %s' "$f"; done)" >&2
    echo "    git add$(for f in $blocked; do printf ' %s' "$f"; done)" >&2
    exit 1
fi

# Safe: for every file needing work, the index and working tree agree.
# shellcheck disable=SC2086
"$reindent" $needs
# shellcheck disable=SC2086
git add $needs

echo "pre-commit: reindented and re-staged:" >&2
for f in $needs; do echo "    $f" >&2; done

exit 0
