#!/usr/bin/env bash
# Pre-commit hook: aplica clang-format als fitxers C++ staged abans del commit.
# - Només toca fitxers staged dins source/ (exclou source/external/ i source/legacy/).
# - Avorta el commit si hi ha canvis NO staged en aquests fitxers (per no incloure'ls sense voler).

set -euo pipefail

if ! command -v clang-format >/dev/null 2>&1; then
    echo "pre-commit: clang-format no trobat — saltant format check" >&2
    exit 0
fi

mapfile -t STAGED < <(git diff --cached --name-only --diff-filter=ACMR \
    | grep -E '^source/.*\.(cpp|hpp|h)$' \
    | grep -vE '^source/(external|legacy)/' || true)

if [ ${#STAGED[@]} -eq 0 ]; then
    exit 0
fi

UNSTAGED_DIRTY=()
for f in "${STAGED[@]}"; do
    if ! git diff --quiet -- "$f"; then
        UNSTAGED_DIRTY+=("$f")
    fi
done

if [ ${#UNSTAGED_DIRTY[@]} -gt 0 ]; then
    echo "pre-commit: aquests fitxers tenen canvis NO staged i estan al commit." >&2
    echo "            Fes 'git add' o 'git stash' abans de continuar:" >&2
    printf '  %s\n' "${UNSTAGED_DIRTY[@]}" >&2
    exit 1
fi

clang-format -i "${STAGED[@]}"
git add -- "${STAGED[@]}"

# --- clang-tidy només sobre els fitxers staged ---
if ! command -v clang-tidy >/dev/null 2>&1; then
    echo "pre-commit: clang-tidy no trobat — saltant tidy" >&2
    exit 0
fi

REPO_ROOT="$(git rev-parse --show-toplevel)"
BUILD_DIR="$REPO_ROOT/build"

if [ ! -f "$BUILD_DIR/compile_commands.json" ]; then
    echo "pre-commit: generant compile_commands.json (build dir buit)..." >&2
    cmake -S "$REPO_ROOT" -B "$BUILD_DIR" >/dev/null
fi

echo "pre-commit: clang-tidy sobre ${#STAGED[@]} fitxer(s)..." >&2
if ! clang-tidy -p "$BUILD_DIR" --quiet "${STAGED[@]}"; then
    echo "pre-commit: clang-tidy ha trobat errors — commit avortat" >&2
    exit 1
fi

# --- cppcheck només sobre els .cpp staged ---
if ! command -v cppcheck >/dev/null 2>&1; then
    echo "pre-commit: cppcheck no trobat — saltant cppcheck" >&2
    exit 0
fi

CPP_STAGED=()
for f in "${STAGED[@]}"; do
    [[ "$f" == *.cpp ]] && CPP_STAGED+=("$f")
done

if [ ${#CPP_STAGED[@]} -eq 0 ]; then
    exit 0
fi

echo "pre-commit: cppcheck sobre ${#CPP_STAGED[@]} fitxer(s)..." >&2
# Nota: el path d'inclusió ha d'anar en relatiu. Amb path absolut, cppcheck
# falla a parsejar "enum class X : std::uint8_t" (no resol <cstdint> bé) i
# emet un syntaxError fals. Els hooks de git s'executen sempre des de la
# rel del repo, així que "source" relatiu és prou.
if ! cppcheck \
        --enable=warning,style,performance,portability \
        --std=c++20 \
        --language=c++ \
        --inline-suppr \
        --suppress=missingIncludeSystem \
        --suppress=toomanyconfigs \
        --suppress='*:*source/external/*' \
        --suppress='*:*source/legacy/*' \
        --suppress=normalCheckLevelMaxBranches \
        --suppress=useStlAlgorithm \
        -D_DEBUG \
        -DLINUX_BUILD \
        --quiet \
        --error-exitcode=1 \
        -I source \
        "${CPP_STAGED[@]}"; then
    echo "pre-commit: cppcheck ha trobat errors — commit avortat" >&2
    exit 1
fi
