#!/usr/bin/env bash
# scripts/git-hooks/pre-commit — drop-in pre-commit hook.
#
# The canonical install path is lefthook (see scripts/install-hooks.sh, which
# detects lefthook and runs `lefthook install`). This file is the fallback when
# lefthook isn't available, copied directly to .git/hooks/pre-commit.
#
# Source of truth for the check list is lefthook.yml. This script must stay in
# sync with it. Changes here also belong in lefthook.yml.
#
# Policy: docs/plans/How We Ship — Engineering Process Policy (v1.0).md §5
# Pilot:  docs/plans/2026-05-15-feat-how-we-ship-mp-1202-pilot-plan.md (sub-phase A)
#
# Skipping in emergencies:
#     git commit --no-verify
# Do not normalise this — CI will catch what you skipped, slower and louder.

set -uo pipefail

TARGET_SECONDS=30
START_TS=$(date +%s)
FAILURES=0

step() { printf "▸ %s\n" "$1"; }
fail() { printf "  ✗ %s\n" "$1"; FAILURES=$((FAILURES+1)); }

STAGED_ALL=$(git diff --cached --name-only --diff-filter=ACMR)
STAGED_PHP=$(echo "$STAGED_ALL" | grep -E '\.php$' || true)
STAGED_COMPOSER=$(echo "$STAGED_ALL" | grep -E '^composer\.(json|lock)$' || true)
STAGED_WORKFLOWS=$(echo "$STAGED_ALL" | grep -E '^\.github/workflows/.*\.ya?ml$' || true)

[ -z "$STAGED_ALL" ] && { echo "No staged changes."; exit 0; }

# 1. PHP syntax
if [ -n "$STAGED_PHP" ]; then
    step "PHP syntax"
    if [ -x vendor/bin/parallel-lint ]; then
        echo "$STAGED_PHP" | xargs vendor/bin/parallel-lint --no-progress --no-colors >/dev/null \
            || fail "php syntax errors"
    else
        echo "$STAGED_PHP" | xargs -I{} php -l {} >/dev/null 2>&1 || fail "php syntax errors"
    fi
fi

# 2. PHPCS
if [ -n "$STAGED_PHP" ]; then
    step "PHPCS (PSR-12)"
    ./scripts/checks/phpcs.sh "$STAGED_PHP" || fail "PSR-12 violations"
fi

# 3. PHPStan
if [ -n "$STAGED_PHP" ]; then
    step "PHPStan (level 4)"
    ./scripts/checks/phpstan.sh "$STAGED_PHP" || fail "PHPStan errors"
fi

# 4. Codeception scoped
if [ -n "$STAGED_PHP" ]; then
    step "Codeception (scoped)"
    ./scripts/checks/codeception-scoped.sh "$STAGED_PHP" || fail "test failures"
fi

# 5. Composer validate (only on lock changes). composer-audit moved to pre-push.
if [ -n "$STAGED_COMPOSER" ]; then
    step "Composer validate"
    composer validate --check-lock --no-check-publish || fail "composer lock out of sync"
fi

# 6. No debug dumps. Pattern uses [v]ar_dump trick so the regex source doesn't
#    itself match the regex (otherwise the hook would reject its own commit).
#    `die(` and `exit(` are NOT in the pattern — both have legitimate uses
#    in legacy Yii2 (action redirects, shutdown handlers).
step "No debug dumps"
pat='^\+.*\b([v]ar_dump|[p]rint_r|[d]d\(|[d]ump\(|[c]onsole\.log)\b'
hits=$(git diff --cached --diff-filter=ACMR -- ':(exclude)*/tests/*' ':(exclude)scripts/git-hooks/*' ':(exclude)scripts/checks/*' ':(exclude)lefthook.yml' | grep -E "$pat" || true)
if [ -n "$hits" ]; then
    echo "$hits"
    fail "debug dumps in staged diff"
fi

# 7. Workflow YAML
if [ -n "$STAGED_WORKFLOWS" ]; then
    step "Workflow lint"
    if command -v actionlint >/dev/null 2>&1; then
        echo "$STAGED_WORKFLOWS" | xargs actionlint || fail "workflow lint failures"
    else
        echo "  (actionlint not installed; CI will catch)"
    fi
fi

# 8. Branch name (§2 model; warning-only for legacy MP-XXXX branches during pilot)
step "Branch + Jira"
BRANCH=$(git rev-parse --abbrev-ref HEAD)
case "$BRANCH" in
    main|HEAD) ;;
    version/[0-9]*.[0-9]*.[0-9]*) ;;
    feature/*/MP-[0-9]*) ;;
    # `case` uses shell glob semantics, not regex. `[a-z0-9-]` matches exactly
    # one char, then `*` matches the rest — so `feature/` (empty suffix) is
    # rejected. Verified via scripts/checks/_test-branch-name.sh.
    feature/[a-z0-9-]*) ;;
    hotfix/[0-9]*.[0-9]*.[0-9]*.[0-9]*/MP-[0-9]*) ;;
    MP-[0-9]*|feat/MP-[0-9]*-*)
        printf "  ⚠ Branch '%s' is a pilot-era legacy name (allowed during pilot only)\n" "$BRANCH"
        ;;
    *)
        printf "  ✗ Branch '%s' does not match §2 branch model\n" "$BRANCH"
        echo "    main | version/X.Y.Z | feature/<name>[/<MP-key>] | hotfix/X.Y.Z.A/<MP-key>"
        fail "branch name format"
        ;;
esac

END_TS=$(date +%s)
ELAPSED=$((END_TS - START_TS))
echo
if [ "$FAILURES" -eq 0 ]; then
    printf "✓ pre-commit clean (%ds)\n" "$ELAPSED"
else
    printf "✗ pre-commit failed: %d check(s) (%ds)\n" "$FAILURES" "$ELAPSED"
fi
if [ "$ELAPSED" -gt "$TARGET_SECONDS" ]; then
    printf "! took %ds (target %ds) — raise in #eng-process\n" "$ELAPSED" "$TARGET_SECONDS"
fi

# Telemetry — gitignored.
printf "%s\t%s\t%d\t%d\n" \
    "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
    "$BRANCH" \
    "$ELAPSED" \
    "$FAILURES" \
    >> .git/hook-telemetry.log 2>/dev/null || true

exit "$FAILURES"
