#!/usr/bin/env bash
set -euo pipefail

# Bypass: git commit --no-verify, or SKIP_LINT=1 git commit ...
if [[ "${SKIP_LINT:-0}" == "1" ]]; then
    echo "pre-commit: SKIP_LINT=1 — skipping lint checks."
    exit 0
fi

PHPCS="vendor/bin/phpcs"
PHPCBF="vendor/bin/phpcbf"
STANDARD="phpcs.xml"
PHPCS_IGNORE="*/vendor/*,*/node_modules/*,*/build/*,*/dist/*,*/tests/*,phpstan-bootstrap.php,*/EDD_SL_Plugin_Updater.php"
FAILED=0

# Auto-fixers re-stage whole files: partially staged files (git add -p) would
# get fully staged, silently including hunks the developer left out.
PARTIALLY_STAGED=$(comm -12 \
    <(git diff --cached --name-only --diff-filter=ACM | sort) \
    <(git diff --name-only | sort) || true)
if [[ -n "$PARTIALLY_STAGED" ]]; then
    echo "pre-commit: these files are partially staged (staged + unstaged changes):" >&2
    echo "$PARTIALLY_STAGED" | sed 's/^/  /' >&2
    echo "pre-commit: auto-fix would stage them entirely. Stage fully or stash unstaged changes first." >&2
    exit 1
fi

# ── PHP ───────────────────────────────────────────────────────────────────────
STAGED_PHP=()
while IFS= read -r f; do [[ -n "$f" ]] && STAGED_PHP+=("$f"); done < <(
    git diff --cached --name-only --diff-filter=ACM | grep '\.php$' || true)

if [[ ${#STAGED_PHP[@]} -gt 0 ]]; then
    if [[ ! -f "$PHPCS" ]]; then
        echo "pre-commit: phpcs not found — run: composer install" >&2
        exit 1
    fi

    # 1. Auto-fix with phpcbf, then re-stage
    echo "pre-commit: phpcbf auto-fixing staged PHP files..."
    "$PHPCBF" --standard="$STANDARD" --ignore="$PHPCS_IGNORE" "${STAGED_PHP[@]}" >/dev/null 2>&1 || true
    git add "${STAGED_PHP[@]}"

    # 2. Capture staged diff for line-level filtering
    export PHPCS_DIFF
    PHPCS_DIFF=$(git diff --cached -U0 -- "${STAGED_PHP[@]}")

    # 3. Run phpcs and filter violations to diff lines only
    # -q suppresses progress output that would corrupt the JSON report.
    # phpcs exits non-zero on any whole-file violation, so capture output
    # first and let the diff filter alone decide pass/fail.
    echo "pre-commit: checking staged PHP lines with phpcs..."
    PHPCS_JSON=$("$PHPCS" -q --standard="$STANDARD" --ignore="$PHPCS_IGNORE" --report=json "${STAGED_PHP[@]}" 2>/dev/null || true)
    echo "$PHPCS_JSON" | php bin/phpcs-diff-filter.php || FAILED=1
fi

# ── JS (non-minified, non-build) ──────────────────────────────────────────────
STAGED_JS=()
while IFS= read -r f; do [[ -n "$f" ]] && STAGED_JS+=("$f"); done < <(
    git diff --cached --name-only --diff-filter=ACM \
    | grep '\.js$' | grep -v '\.min\.js$' | grep -v 'build/' \
    | grep -v 'node_modules' || true)

STAGED_STYLES=()
while IFS= read -r f; do [[ -n "$f" ]] && STAGED_STYLES+=("$f"); done < <(
    git diff --cached --name-only --diff-filter=ACM | grep -E '\.(scss|less)$' || true)

if [[ ${#STAGED_JS[@]} -gt 0 || ${#STAGED_STYLES[@]} -gt 0 ]]; then
    if [[ ! -d "node_modules/@wordpress/scripts" ]]; then
        echo "pre-commit: wp-scripts not found — run: npm install" >&2
        exit 1
    fi
fi

if [[ ${#STAGED_JS[@]} -gt 0 ]]; then
    echo "pre-commit: linting staged JS files..."
    # eslint called directly (not wp-scripts lint-js: `--fix <file>` makes the
    # wrapper swallow the path and rewrite its entire default glob).
    # Config: root .eslintrc.json — recommended (prettier) for React targets,
    # custom+esnext override for vanilla JS dirs.
    npx eslint --fix "${STAGED_JS[@]}" >/dev/null 2>&1 || true
    git add "${STAGED_JS[@]}"
    npx eslint "${STAGED_JS[@]}" || FAILED=1
fi

# ── SCSS / LESS ───────────────────────────────────────────────────────────────
if [[ ${#STAGED_STYLES[@]} -gt 0 ]]; then
    echo "pre-commit: linting staged SCSS/LESS files..."
    # stylelint called directly: wp-scripts lint-style ignores file arguments
    # when fixing and rewrites its entire default glob (including build CSS).
    npx stylelint --fix "${STAGED_STYLES[@]}" >/dev/null 2>&1 || true
    git add "${STAGED_STYLES[@]}"
    npx stylelint "${STAGED_STYLES[@]}" || FAILED=1
fi

# ── Result ────────────────────────────────────────────────────────────────────
if [[ $FAILED -ne 0 ]]; then
    echo "" >&2
    echo "pre-commit: fix errors above before committing." >&2
    exit 1
fi

exit 0
