#!/bin/bash
# Tracked source of the repository pre-commit gate.
# Activate in a fresh clone with: git config core.hooksPath .dev-config/hooks

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

IPZ_DIR="plugins/international-press-zone"
FAILED=0
NOT_ANALYSED=()

fail() {
    echo "❌ $*" >&2
    FAILED=1
}

note_unanalysed() {
    NOT_ANALYSED+=("$1")
}

echo "🛑 Running Pre-Commit Checks..."

# 1. JavaScript (ESLint) — staged JS/TS only
if [ -f "package.json" ]; then
    mapfile -d '' -t STAGED_JS < <(git diff --cached -z --name-only --diff-filter=ACM -- '*.js' '*.jsx' '*.ts' '*.tsx')
    if [ ${#STAGED_JS[@]} -gt 0 ]; then
        echo "🔍 ESLint — ${#STAGED_JS[@]} staged file(s)..."
        npx eslint --config .dev-config/eslint.config.mjs --no-warn-ignored --max-warnings 9999 -- "${STAGED_JS[@]}"
        rc=$?
        case "$rc" in
            0) ;;
            1) fail "ESLint reported problems in staged files" ;;
            *) fail "ESLint could not run (exit $rc)" ;;
        esac
    else
        note_unanalysed "JavaScript/TypeScript — no staged files"
    fi
else
    note_unanalysed "JavaScript/TypeScript — no package.json at repository root"
fi

# 2. PHP static analysis — international-press-zone is the only plugin with configs
mapfile -d '' -t STAGED_PHP < <(git diff --cached -z --name-only --diff-filter=ACM -- '*.php')

IPZ_PHP=()
OTHER_PHP=()
for f in "${STAGED_PHP[@]}"; do
    [ -f "$f" ] || continue
    case "$f" in
        "$IPZ_DIR"/*) IPZ_PHP+=("${f#"$IPZ_DIR"/}") ;;
        *) OTHER_PHP+=("$f") ;;
    esac
done

if [ ${#OTHER_PHP[@]} -gt 0 ]; then
    note_unanalysed "PHP — ${#OTHER_PHP[@]} staged file(s) outside $IPZ_DIR (no static-analysis config exists for them)"
fi

in_scope() {
    local p="$1"
    shift
    local r
    for r in "$@"; do
        r="${r%/}"
        [ "$p" = "$r" ] && return 0
        case "$p" in "$r"/*) return 0 ;; esac
    done
    return 1
}

if [ ${#IPZ_PHP[@]} -gt 0 ]; then
    for required in phpcs.xml.dist phpcs-baseline.json tools/phpcs-baseline.php phpstan.neon.dist phpstan-baseline.neon vendor/bin/phpcs vendor/bin/phpstan; do
        [ -e "$IPZ_DIR/$required" ] || fail "PHP gate could not run: $IPZ_DIR/$required is missing"
    done

    mapfile -t PHPCS_ROOTS < <(sed -n 's|.*<file>\(.*\)</file>.*|\1|p' "$IPZ_DIR/phpcs.xml.dist" 2>/dev/null)
    [ ${#PHPCS_ROOTS[@]} -gt 0 ] || fail "PHP gate could not run: no <file> scope found in $IPZ_DIR/phpcs.xml.dist"

    mapfile -t PHPSTAN_ROOTS < <(awk '/^[[:space:]]*paths:[[:space:]]*$/{f=1;next} f&&/^[[:space:]]*-[[:space:]]/{sub(/^[[:space:]]*-[[:space:]]*/,"");print;next} f{exit}' "$IPZ_DIR/phpstan.neon.dist" 2>/dev/null)
    [ ${#PHPSTAN_ROOTS[@]} -gt 0 ] || fail "PHP gate could not run: no paths: scope found in $IPZ_DIR/phpstan.neon.dist"

    if [ "$FAILED" -ne 0 ]; then
        echo "❌ Pre-commit gate FAILED — a required PHP check could not run." >&2
        exit 1
    fi

    CS_FILES=()
    STAN_FILES=()
    CS_OUT=0
    STAN_OUT=0
    for p in "${IPZ_PHP[@]}"; do
        if in_scope "$p" "${PHPCS_ROOTS[@]}"; then CS_FILES+=("$p"); else CS_OUT=$((CS_OUT + 1)); fi
        if in_scope "$p" "${PHPSTAN_ROOTS[@]}"; then STAN_FILES+=("$p"); else STAN_OUT=$((STAN_OUT + 1)); fi
    done

    if [ ${#CS_FILES[@]} -gt 0 ]; then
        echo "🔍 PHPCS (international-press-zone, baselined) — ${#CS_FILES[@]} staged file(s)..."
        (cd "$IPZ_DIR" && php tools/phpcs-baseline.php check "${CS_FILES[@]}")
        rc=$?
        case "$rc" in
            0) ;;
            1) fail "PHPCS reported violations beyond the baseline in staged $IPZ_DIR files" ;;
            *) fail "PHPCS could not run (exit $rc)" ;;
        esac
    fi
    [ "$CS_OUT" -gt 0 ] && note_unanalysed "PHPCS — $CS_OUT staged $IPZ_DIR file(s) outside the ruleset <file> scope"

    if [ ${#STAN_FILES[@]} -gt 0 ]; then
        echo "🔍 PHPStan (international-press-zone, baselined) — ${#STAN_FILES[@]} staged file(s)..."
        (cd "$IPZ_DIR" && vendor/bin/phpstan analyse --configuration=phpstan.neon.dist --no-progress -- "${STAN_FILES[@]}")
        rc=$?
        case "$rc" in
            0) ;;
            1) fail "PHPStan reported errors in staged $IPZ_DIR files" ;;
            *) fail "PHPStan could not run (exit $rc)" ;;
        esac
    fi
    [ "$STAN_OUT" -gt 0 ] && note_unanalysed "PHPStan — $STAN_OUT staged $IPZ_DIR file(s) outside the config paths: scope"
else
    note_unanalysed "PHP — no staged files under $IPZ_DIR"
fi

# 3. Filesystem vulnerabilities / secrets
if command -v trivy &>/dev/null; then
    echo "🔍 Trivy — filesystem scan..."
    trivy fs . --scanners vuln,secret,misconfig --exit-code 1 || fail "Trivy reported findings"
else
    note_unanalysed "Trivy — binary not installed"
fi

# 4. Composer dependency audit
if [ -f "composer.lock" ]; then
    if command -v composer &>/dev/null; then
        echo "🔍 Composer audit..."
        composer audit || fail "Composer audit reported advisories"
    else
        note_unanalysed "Composer audit — composer binary not installed"
    fi
else
    note_unanalysed "Composer audit — no composer.lock at repository root"
fi

if [ "$FAILED" -ne 0 ]; then
    echo "❌ Pre-commit gate FAILED." >&2
    exit 1
fi

echo "✅ Every check that applies to the staged changes passed."
for entry in "${NOT_ANALYSED[@]}"; do
    echo "ℹ️  Not analysed: $entry"
done
exit 0
# slopgate-hook v1 BEGIN
SLOPGATE_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
if [ -n "$SLOPGATE_ROOT" ] && [ -f "$SLOPGATE_ROOT/.slopgate/config.toml" ]; then
  SLOPGATE_ENGINE="${SLOPGATE_BIN:-$(command -v slopgate)}"
  if [ -z "$SLOPGATE_ENGINE" ]; then
    echo "slopgate: engine not found on PATH (install slopgate or set SLOPGATE_BIN) — commit BLOCKED" >&2
    exit 1
  fi
  "$SLOPGATE_ENGINE" --staged --config "$SLOPGATE_ROOT/.slopgate/config.toml" || exit 1
fi
# slopgate-hook v1 END
