#!/bin/sh
# Pre-commit hook for Signal Fish Server
# Runs code formatting, linting, and validation checks before each commit.
#
# To install: ./scripts/enable-hooks.sh
# To bypass (not recommended): git commit --no-verify
#
# Checks performed:
#   1. Code formatting (cargo fmt)
#   2. Clippy lints (cargo clippy)
#   3. Panic-prone patterns
#   4. MSRV consistency (if Cargo.toml or config files modified)
#   5. Workflow AWK scripts (if workflow files modified)
#   6. YAML lint (if workflow files modified)
#   7. AWK file syntax (if .awk files modified)
#   8. Shellcheck for CI scripts (if .github/scripts/ files modified)
#   9. Markdown relative link validation (if docs/ markdown modified)
#  10. Markdown linting (if markdown files modified)
#  11. Link checking (if markdown files modified)
#  12. Markdown code block validation (if markdown files modified)
#  13. British English spellings (warning only)
#  14. Path-filtered workflow registration (if workflow files modified)
#  15. Schedule trigger guards (if workflow files modified)
#  16. Skills index generation freshness (if skills inputs/script/context change)
#  17. Workflow hygiene script checks (if workflow/hygiene script files modified)
#  18. LLM file size limit (if .llm/ files modified — max 300 lines per file)
#  19. Miri compatibility (if Rust source files modified)

set -e

# Color output helpers
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Disable colors if not a TTY
if [ ! -t 1 ]; then
    RED=''
    GREEN=''
    YELLOW=''
    BLUE=''
    NC=''
fi

TOTAL_CHECKS=19

echo "${BLUE}[pre-commit]${NC} Running pre-commit checks..."
echo ""

CHECKS_PASSED=0
CHECKS_FAILED=0

# Helper to report check results
check_pass() {
    echo "${GREEN}✓ PASS${NC}: $1"
    CHECKS_PASSED=$((CHECKS_PASSED + 1))
}

check_fail() {
    echo "${RED}✗ FAIL${NC}: $1"
    echo "${RED}[pre-commit] ERROR:${NC} $2"
    CHECKS_FAILED=$((CHECKS_FAILED + 1))
}

check_warn() {
    echo "${YELLOW}⚠ WARN${NC}: $1"
    echo "${YELLOW}[pre-commit] WARNING:${NC} $2"
    CHECKS_PASSED=$((CHECKS_PASSED + 1))
}

check_skip() {
    echo "${YELLOW}⊘ SKIP${NC}: $1 ($2)"
}

# Check 1: Code formatting (always run)
echo "${BLUE}[1/${TOTAL_CHECKS}]${NC} Checking code formatting..."
if cargo fmt --check 2>/dev/null; then
    check_pass "Code formatting"
else
    check_fail "Code formatting" "Run 'cargo fmt' to fix formatting issues."
    echo "${YELLOW}Tip:${NC} cargo fmt --all"
    echo ""
fi

# Check 2: Clippy lints (fast check on staged Rust files only)
echo "${BLUE}[2/${TOTAL_CHECKS}]${NC} Running clippy on staged files..."
# Get list of staged Rust files
STAGED_RS_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.rs$' || true)

if [ -n "$STAGED_RS_FILES" ]; then
    # Run clippy on workspace (faster than per-file)
    # Use --all-features to catch all issues, but allow a longer timeout
    if ! cargo clippy --all-targets --all-features -- -D warnings 2>&1; then
        check_fail "Clippy lints" "Fix clippy warnings before committing."
        echo "${YELLOW}Tip:${NC} cargo clippy --fix --allow-dirty --all-targets --all-features"
        echo ""
    else
        check_pass "Clippy lints"
    fi
else
    check_skip "Clippy lints" "no Rust files staged"
fi

# Check 3: Panic-prone patterns (fast pattern scan only)
echo "${BLUE}[3/${TOTAL_CHECKS}]${NC} Checking for panic-prone patterns..."
if [ -x scripts/check-no-panics.sh ]; then
    if scripts/check-no-panics.sh patterns 2>&1 | grep -q "ERROR"; then
        check_fail "Panic patterns" "Remove .unwrap(), panic!(), expect() from production code."
        echo ""
    else
        check_pass "Panic patterns"
    fi
else
    check_skip "Panic patterns" "check-no-panics.sh not found or not executable"
fi

# Check 4: MSRV consistency (only if relevant files changed)
echo "${BLUE}[4/${TOTAL_CHECKS}]${NC} Checking MSRV consistency..."
MSRV_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '(Cargo.toml|rust-toolchain.toml|clippy.toml|Dockerfile)$' || true)

if [ -n "$MSRV_FILES" ]; then
    if [ -x scripts/check-msrv-consistency.sh ]; then
        if scripts/check-msrv-consistency.sh 2>&1 | grep -q "FAILED"; then
            check_fail "MSRV consistency" "MSRV mismatch across configuration files."
            echo "${YELLOW}Tip:${NC} ./scripts/check-msrv-consistency.sh for details"
            echo ""
        else
            check_pass "MSRV consistency"
        fi
    else
        check_skip "MSRV consistency" "check-msrv-consistency.sh not found"
    fi
else
    check_skip "MSRV consistency" "no MSRV-related files changed"
fi

# Check 5: Workflow AWK validation (only if workflow files changed)
echo "${BLUE}[5/${TOTAL_CHECKS}]${NC} Validating AWK scripts in workflows..."
WORKFLOW_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.github/workflows/.*\.ya?ml$' || true)

if [ -n "$WORKFLOW_FILES" ]; then
    if [ -x scripts/validate-workflow-awk.sh ]; then
        # Pass the specific workflow files to check
        if echo "$WORKFLOW_FILES" | xargs scripts/validate-workflow-awk.sh 2>&1 | grep -q "FAILED"; then
            check_fail "AWK validation" "AWK script syntax errors in workflow files."
            echo "${YELLOW}Tip:${NC} ./scripts/validate-workflow-awk.sh"
            echo ""
        else
            check_pass "AWK validation"
        fi
    else
        check_skip "AWK validation" "validate-workflow-awk.sh not found"
    fi
else
    check_skip "AWK validation" "no workflow files changed"
fi

# Check 6: YAML lint (only if workflow files changed)
echo "${BLUE}[6/${TOTAL_CHECKS}]${NC} Running yamllint on workflow files..."

if [ -n "$WORKFLOW_FILES" ]; then
    if command -v yamllint >/dev/null 2>&1; then
        YAMLLINT_ERRORS=0
        for wf_file in $WORKFLOW_FILES; do
            if [ -f "$wf_file" ]; then
                YAMLLINT_OUT=$(yamllint -c .yamllint.yml "$wf_file" 2>&1)
                if echo "$YAMLLINT_OUT" | grep -q "\[error\]"; then
                    echo "${RED}  YAML lint errors:${NC} $wf_file"
                    echo "$YAMLLINT_OUT" | grep "\[error\]" || true
                    YAMLLINT_ERRORS=$((YAMLLINT_ERRORS + 1))
                fi
            fi
        done
        if [ "$YAMLLINT_ERRORS" -gt 0 ]; then
            check_fail "YAML lint" "Fix YAML lint errors in workflow files."
            echo "${YELLOW}Tip:${NC} yamllint -c .yamllint.yml .github/workflows/"
            echo ""
        else
            check_pass "YAML lint"
        fi
    else
        check_skip "YAML lint" "yamllint not installed (pip install yamllint)"
    fi
else
    check_skip "YAML lint" "no workflow files changed"
fi

# Check 7: AWK file syntax (only if .awk files are staged)
echo "${BLUE}[7/${TOTAL_CHECKS}]${NC} Validating AWK file syntax..."
STAGED_AWK_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.awk$' || true)

if [ -n "$STAGED_AWK_FILES" ]; then
    AWK_ERRORS=0
    for awk_file in $STAGED_AWK_FILES; do
        if [ -f "$awk_file" ]; then
            if ! awk -f "$awk_file" < /dev/null > /dev/null 2>&1; then
                echo "${RED}  AWK syntax error:${NC} $awk_file"
                awk -f "$awk_file" < /dev/null 2>&1 || true
                AWK_ERRORS=$((AWK_ERRORS + 1))
            fi
        fi
    done
    if [ "$AWK_ERRORS" -gt 0 ]; then
        check_fail "AWK file syntax" "Fix AWK syntax errors in staged .awk files."
        echo ""
    else
        check_pass "AWK file syntax"
    fi
else
    check_skip "AWK file syntax" "no .awk files staged"
fi

# Check 8: Shellcheck for CI scripts (only if .github/scripts/ files are staged)
echo "${BLUE}[8/${TOTAL_CHECKS}]${NC} Running shellcheck on CI scripts..."
STAGED_CI_SCRIPTS=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.github/scripts/.*\.sh$' || true)

if [ -n "$STAGED_CI_SCRIPTS" ]; then
    if command -v shellcheck >/dev/null 2>&1; then
        SC_ERRORS=0
        for script in $STAGED_CI_SCRIPTS; do
            if [ -f "$script" ]; then
                if ! shellcheck -s bash "$script" > /dev/null 2>&1; then
                    echo "${RED}  shellcheck errors:${NC} $script"
                    shellcheck -s bash "$script" 2>&1 | head -10
                    SC_ERRORS=$((SC_ERRORS + 1))
                fi
            fi
        done
        if [ "$SC_ERRORS" -gt 0 ]; then
            check_fail "Shellcheck CI scripts" "Fix shellcheck errors in .github/scripts/."
            echo ""
        else
            check_pass "Shellcheck CI scripts"
        fi
    else
        check_skip "Shellcheck CI scripts" "shellcheck not installed"
    fi
else
    check_skip "Shellcheck CI scripts" "no .github/scripts/*.sh files staged"
fi

# Check 9: Markdown relative link validation (only if docs/ markdown files are staged)
echo "${BLUE}[9/${TOTAL_CHECKS}]${NC} Validating markdown relative links in docs/..."
STAGED_DOCS_MD=$(git diff --cached --name-only --diff-filter=ACM | grep -E '^docs/.*\.md$' || true)

if [ -n "$STAGED_DOCS_MD" ]; then
    LINK_ERRORS=0
    for md_file in $STAGED_DOCS_MD; do
        if [ -f "$md_file" ]; then
            base_dir=$(dirname "$md_file")
            # Extract relative links with fence-aware AWK (skips links inside code blocks
            # and inline code spans to avoid false positives on example links).
            for url in $(awk '
              /^```/ {
                if (!fence_width) {
                  n=0;s=$0;while(substr(s,1,1)=="`"){n++;s=substr(s,2)};fence_width=n
                } else {
                  n=0;s=$0;while(substr(s,1,1)=="`"){n++;s=substr(s,2)}
                  if(n>=fence_width&&s~/^[ \t]*$/)fence_width=0
                }
                next
              }
              !fence_width { line=$0; gsub(/`[^`]+`/,"",line)
                while(match(line,/\[([^]]+)\]\(([^)]+)\)/)){
                  u=substr(line,RSTART,RLENGTH);sub(/.*\(/,"",u);sub(/\).*/,"",u);print u
                  line=substr(line,RSTART+RLENGTH)
                }
              }
            ' "$md_file" | grep -vE '^(https?://|mailto:|#)' || true); do
                # Strip anchor for file check
                file_part="${url%%#*}"
                [ -z "$file_part" ] && continue

                resolved="$base_dir/$file_part"
                if [ ! -f "$resolved" ] && [ ! -d "$resolved" ]; then
                    echo "${RED}  Broken link:${NC} $md_file -> $url"
                    LINK_ERRORS=$((LINK_ERRORS + 1))
                fi

                # Check for .llm/ links missing ../ prefix
                case "$url" in
                    .llm/*)
                        echo "${RED}  Invalid prefix:${NC} $md_file -> $url (should be ../$url)"
                        LINK_ERRORS=$((LINK_ERRORS + 1))
                        ;;
                esac
            done
        fi
    done
    if [ "$LINK_ERRORS" -gt 0 ]; then
        check_fail "Docs link validation" "Fix broken or incorrect relative links in docs/."
        echo "${YELLOW}Tip:${NC} ./scripts/validate-ci.sh --links"
        echo ""
    else
        check_pass "Docs link validation"
    fi
else
    check_skip "Docs link validation" "no docs/*.md files staged"
fi

# Check 10: Markdown linting (only if markdown files changed)
echo "${BLUE}[10/${TOTAL_CHECKS}]${NC} Checking markdown files..."
STAGED_MD_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.md$' || true)

if [ -n "$STAGED_MD_FILES" ]; then
    if command -v markdownlint-cli2 >/dev/null 2>&1; then
        if [ -x scripts/check-markdown.sh ]; then
            if scripts/check-markdown.sh 2>&1 | grep -q "failed"; then
                check_fail "Markdown linting" "Markdown files have formatting issues."
                echo "${YELLOW}Tip:${NC} ./scripts/check-markdown.sh fix"
                echo ""
            else
                check_pass "Markdown linting"
            fi
        else
            check_skip "Markdown linting" "check-markdown.sh not found"
        fi
    else
        check_skip "Markdown linting" "markdownlint-cli2 not installed"
    fi
else
    check_skip "Markdown linting" "no markdown files changed"
fi

# Check 11: Link checking (only if markdown files changed)
echo "${BLUE}[11/${TOTAL_CHECKS}]${NC} Checking links in markdown files..."

if [ -n "$STAGED_MD_FILES" ]; then
    if command -v lychee >/dev/null 2>&1; then
        # Use a temporary file to avoid issues with filenames containing spaces
        TEMP_FILE=$(mktemp)
        echo "$STAGED_MD_FILES" > "$TEMP_FILE"
        # Run lychee with fast settings (offline mode for pre-commit speed)
        if ! xargs -a "$TEMP_FILE" lychee --offline --quiet --config .lychee.toml 2>/dev/null; then
            echo "${YELLOW}⚠ WARNING${NC}: Link check found issues in staged markdown files."
            echo "${YELLOW}Note:${NC} This is a warning only (offline mode)."
            echo "${YELLOW}Tip:${NC} Run 'lychee --config .lychee.toml <file>' for full check."
            # Non-blocking warning
        else
            check_pass "Link checking (offline)"
        fi
        rm -f "$TEMP_FILE"
    else
        check_skip "Link checking" "lychee not installed"
    fi
else
    check_skip "Link checking" "no markdown files changed"
fi

# Check 12: Markdown code block validation (only if markdown files changed)
# Validates JSON (jq), YAML (yq), TOML (taplo), and Bash (bash -n) code blocks
# extracted from staged markdown files, matching the CI doc-validation.yml workflow.
echo "${BLUE}[12/${TOTAL_CHECKS}]${NC} Validating code blocks in markdown files..."

if [ -n "$STAGED_MD_FILES" ]; then
    # Filter out test fixture directories from staged markdown files
    CB_MD_FILES=$(echo "$STAGED_MD_FILES" | grep -vE '^(\.github/test-fixtures/|test-fixtures/)' || true)

    if [ -n "$CB_MD_FILES" ]; then
        CB_TEMP_DIR=$(mktemp -d)
        # Use file-based counters to avoid subshell scope issues
        CB_COUNTER_FILE="$CB_TEMP_DIR/counters"
        # Counter file format: 2 space-separated integers (total failed)
        echo "0 0" > "$CB_COUNTER_FILE"

        # AWK program that extracts code blocks and writes each to a file.
        # This matches the fence tracking pattern used in doc-validation.yml:
        #   - Outer fences (4+ backticks) skip nested 3-backtick examples
        #   - Closing fences allow trailing spaces/tabs per CommonMark spec
        #
        # AWK writes each block to OUTDIR/block_NNN.EXT and emits
        # "line_number<TAB>filename" to stdout for the shell to process.
        # This avoids NUL-delimited reads (read -d is a bashism, not POSIX sh).
        #
        # Parameters (via -v):
        #   LANG_PATTERN  - regex to match opening fence tags
        #   OUTDIR        - directory to write extracted block files
        #   EXT           - file extension for extracted blocks
        EXTRACT_BLOCKS_AWK='
          # Skip content inside outer fences (4+ backticks) which may contain
          # nested 3-backtick examples (e.g., documentation about code blocks).
          /^````/ {
            if (!outer_fence) {
              n = 0; s = $0; while (substr(s,1,1)=="`") { n++; s = substr(s,2) }
              outer_fence = n
            } else {
              n = 0; s = $0; while (substr(s,1,1)=="`") { n++; s = substr(s,2) }
              if (n >= outer_fence && s ~ /^[ \t]*$/) outer_fence = 0
            }
            next
          }
          outer_fence { next }
          # Match opening fence for target language code blocks
          $0 ~ LANG_PATTERN {
            in_block = 1
            block_start = NR
            block_num++
            content = ""
            next
          }
          # Match closing fence only if in a block
          # CommonMark: closing fences may have trailing spaces/tabs
          /^```[ \t]*$/ && in_block {
            if (content != "") {
              outfile = OUTDIR "/block_" block_num "." EXT
              printf "%s", content > outfile
              close(outfile)
              print block_start "\t" outfile
            }
            in_block = 0
            next
          }
          # Accumulate content while in block
          in_block {
            content = content $0 "\n"
          }
          # Handle unclosed blocks at end of file
          END {
            if (in_block && content != "") {
              outfile = OUTDIR "/block_" block_num "." EXT
              printf "%s", content > outfile
              close(outfile)
              print block_start "\t" outfile
            }
          }
        '

        # Check which validation tools are available
        HAS_JQ=0
        HAS_YQ=0
        HAS_TAPLO=0

        if command -v jq >/dev/null 2>&1; then HAS_JQ=1; fi
        if command -v yq >/dev/null 2>&1; then HAS_YQ=1; fi
        if command -v taplo >/dev/null 2>&1; then HAS_TAPLO=1; fi

        # --- Validate JSON code blocks ---
        if [ "$HAS_JQ" -eq 1 ]; then
            for md_file in $CB_MD_FILES; do
                [ -f "$md_file" ] || continue
                awk -v LANG_PATTERN='^```json$' \
                    -v OUTDIR="$CB_TEMP_DIR" \
                    -v EXT="json" \
                    "$EXTRACT_BLOCKS_AWK" "$md_file" | while IFS='	' read -r line_num outfile; do
                    [ -z "$outfile" ] && continue
                    read -r cb_total cb_failed < "$CB_COUNTER_FILE"
                    cb_total=$((cb_total + 1))

                    if ! jq empty "$outfile" 2>/dev/null; then
                        echo "${RED}  Invalid JSON in $md_file at line $line_num${NC}"
                        cb_failed=$((cb_failed + 1))
                    fi

                    echo "$cb_total $cb_failed" > "$CB_COUNTER_FILE"
                done
            done
        fi

        # --- Validate YAML code blocks ---
        if [ "$HAS_YQ" -eq 1 ]; then
            for md_file in $CB_MD_FILES; do
                [ -f "$md_file" ] || continue
                awk -v LANG_PATTERN='^```(yaml|yml)$' \
                    -v OUTDIR="$CB_TEMP_DIR" \
                    -v EXT="yaml" \
                    "$EXTRACT_BLOCKS_AWK" "$md_file" | while IFS='	' read -r line_num outfile; do
                    [ -z "$outfile" ] && continue
                    read -r cb_total cb_failed < "$CB_COUNTER_FILE"
                    cb_total=$((cb_total + 1))

                    if ! yq eval . "$outfile" >/dev/null 2>&1; then
                        echo "${RED}  Invalid YAML in $md_file at line $line_num${NC}"
                        cb_failed=$((cb_failed + 1))
                    fi

                    echo "$cb_total $cb_failed" > "$CB_COUNTER_FILE"
                done
            done
        fi

        # --- Validate TOML code blocks (skip if taplo not available) ---
        if [ "$HAS_TAPLO" -eq 1 ]; then
            for md_file in $CB_MD_FILES; do
                [ -f "$md_file" ] || continue
                awk -v LANG_PATTERN='^```toml$' \
                    -v OUTDIR="$CB_TEMP_DIR" \
                    -v EXT="toml" \
                    "$EXTRACT_BLOCKS_AWK" "$md_file" | while IFS='	' read -r line_num outfile; do
                    [ -z "$outfile" ] && continue
                    read -r cb_total cb_failed < "$CB_COUNTER_FILE"
                    cb_total=$((cb_total + 1))

                    if ! taplo check "$outfile" 2>/dev/null; then
                        echo "${RED}  Invalid TOML in $md_file at line $line_num${NC}"
                        cb_failed=$((cb_failed + 1))
                    fi

                    echo "$cb_total $cb_failed" > "$CB_COUNTER_FILE"
                done
            done
        fi

        # --- Validate Bash code blocks (bash -n syntax check, always available) ---
        for md_file in $CB_MD_FILES; do
            [ -f "$md_file" ] || continue
            awk -v LANG_PATTERN='^```(bash|sh|shell)$' \
                -v OUTDIR="$CB_TEMP_DIR" \
                -v EXT="sh" \
                "$EXTRACT_BLOCKS_AWK" "$md_file" | while IFS='	' read -r line_num outfile; do
                [ -z "$outfile" ] && continue
                # Skip placeholder/example-only blocks
                if grep -qE '^#.*example|^# Note:|^\.\.\.$' "$outfile"; then
                    continue
                fi
                read -r cb_total cb_failed < "$CB_COUNTER_FILE"
                cb_total=$((cb_total + 1))

                if ! bash -n "$outfile" 2>/dev/null; then
                    echo "${RED}  Invalid Bash syntax in $md_file at line $line_num${NC}"
                    cb_failed=$((cb_failed + 1))
                fi

                echo "$cb_total $cb_failed" > "$CB_COUNTER_FILE"
            done
        done

        # Read final counters
        read -r cb_total cb_failed < "$CB_COUNTER_FILE"
        rm -rf "$CB_TEMP_DIR"

        # Build a summary of which validators ran
        CB_TOOLS_USED=""
        if [ "$HAS_JQ" -eq 1 ]; then CB_TOOLS_USED="${CB_TOOLS_USED}JSON "; fi
        if [ "$HAS_YQ" -eq 1 ]; then CB_TOOLS_USED="${CB_TOOLS_USED}YAML "; fi
        if [ "$HAS_TAPLO" -eq 1 ]; then CB_TOOLS_USED="${CB_TOOLS_USED}TOML "; fi
        CB_TOOLS_USED="${CB_TOOLS_USED}Bash"

        if [ "$cb_failed" -gt 0 ]; then
            check_fail "Code block validation" "$cb_failed/$cb_total blocks invalid (checked: $CB_TOOLS_USED)"
            echo ""
        else
            if [ "$cb_total" -gt 0 ]; then
                check_pass "Code block validation ($cb_total blocks: $CB_TOOLS_USED)"
            else
                check_pass "Code block validation (no blocks found)"
            fi
        fi
    else
        check_skip "Code block validation" "only test fixture markdown files staged"
    fi
else
    check_skip "Code block validation" "no markdown files changed"
fi

# Check 13: British English spellings (warning only)
# Catches common British spellings that should be American English.
# This is a WARNING (non-blocking) since there may be legitimate uses
# (e.g., external references, proper nouns, quotes).
echo "${BLUE}[13/${TOTAL_CHECKS}]${NC} Checking for British English spellings..."
STAGED_TEXT_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(rs|yml|yaml|md)$' || true)

if [ -n "$STAGED_TEXT_FILES" ]; then
    # Each entry is "british_pattern:american_equivalent"
    BRITISH_WORDS="uninitialised:uninitialized
initialised:initialized
initialise:initialize
initialising:initializing
behaviour:behavior
colour:color
colours:colors
favour:favor
favoured:favored
favouring:favoring
honour:honor
honoured:honored
honouring:honoring
serialise:serialize
serialised:serialized
serialising:serializing
organise:organize
organised:organized
organising:organizing
recognise:recognize
recognised:recognized
recognising:recognizing"

    # Use a temp file to count warnings across subshells (pipe creates subshell)
    BRIT_WARN_FILE=$(mktemp)
    : > "$BRIT_WARN_FILE"
    for text_file in $STAGED_TEXT_FILES; do
        [ -f "$text_file" ] || continue
        LINE_NUM=0
        while IFS= read -r line; do
            LINE_NUM=$((LINE_NUM + 1))
            # Skip lines containing URLs to avoid false positives on external references
            case "$line" in
                *http://*|*https://*) continue ;;
            esac
            echo "$BRITISH_WORDS" | while IFS=: read -r british american; do
                [ -z "$british" ] && continue
                # Case-insensitive match using tr for POSIX compatibility
                line_lower=$(echo "$line" | tr 'A-Z' 'a-z')
                if echo "$line_lower" | grep -qw "$british"; then
                    echo "${YELLOW}  British spelling:${NC} $text_file:$LINE_NUM: found '${british}', prefer '${american}'"
                    echo "1" >> "$BRIT_WARN_FILE"
                fi
            done
        done < "$text_file"
    done
    BRIT_WARNINGS=$(wc -l < "$BRIT_WARN_FILE" | tr -d ' ')
    rm -f "$BRIT_WARN_FILE"

    if [ "$BRIT_WARNINGS" -gt 0 ]; then
        check_warn "British English spellings" "$BRIT_WARNINGS British spelling(s) found. Consider using American English."
    else
        check_pass "British English spellings"
    fi
else
    check_skip "British English spellings" "no .rs/.yml/.yaml/.md files staged"
fi

# Check 14: Path-filtered workflow registration in release preflight
# Ensures any workflow in REQUIRED_WORKFLOWS that uses path filters is also
# registered in PATH_FILTERED_WORKFLOWS in release.yml, so the release
# preflight correctly handles skipped workflow runs.
echo "${BLUE}[14/${TOTAL_CHECKS}]${NC} Checking path-filtered workflow registration..."

RELEASE_FILE=".github/workflows/release.yml"
if [ -n "$WORKFLOW_FILES" ]; then
    if [ -f "$RELEASE_FILE" ]; then
        PF_ERRORS=0

        # For each staged workflow file, check if it has path filters AND
        # its name appears in REQUIRED_WORKFLOWS. If so, it must also
        # appear in PATH_FILTERED_WORKFLOWS.
        for wf_file in $WORKFLOW_FILES; do
            [ -f "$wf_file" ] || continue
            # Skip release.yml itself
            [ "$wf_file" = "$RELEASE_FILE" ] && continue

            # Extract the workflow name
            WF_NAME=$(grep -E '^name:' "$wf_file" | head -1 | sed 's/^name:[[:space:]]*//' | sed 's/^["'"'"']//' | sed 's/["'"'"']$//' || true)
            [ -z "$WF_NAME" ] && continue

            # Check if this workflow uses path filters
            HAS_PATHS=$(grep -c '^[[:space:]]*paths:' "$wf_file" 2>/dev/null || echo "0")

            if [ "$HAS_PATHS" -gt 0 ]; then
                # Check if it's in REQUIRED_WORKFLOWS
                if grep -q "\"$WF_NAME\"" "$RELEASE_FILE" 2>/dev/null; then
                    # Check if it's registered in PATH_FILTERED_WORKFLOWS
                    if ! grep -q "PATH_FILTERED_WORKFLOWS\[\"$WF_NAME\"\]" "$RELEASE_FILE" 2>/dev/null; then
                        echo "${RED}  Missing registration:${NC} Workflow '${WF_NAME}' (${wf_file})"
                        echo "${RED}    Has path filters and is in REQUIRED_WORKFLOWS but not in PATH_FILTERED_WORKFLOWS${NC}"
                        echo "${RED}    Add: PATH_FILTERED_WORKFLOWS[\"${WF_NAME}\"]=\"<paths>\" to release.yml${NC}"
                        PF_ERRORS=$((PF_ERRORS + 1))
                    fi
                fi
            fi
        done

        if [ "$PF_ERRORS" -gt 0 ]; then
            check_fail "Path-filtered workflow registration" "Workflows with path filters in REQUIRED_WORKFLOWS must be registered in PATH_FILTERED_WORKFLOWS in release.yml."
            echo ""
        else
            check_pass "Path-filtered workflow registration"
        fi
    else
        check_skip "Path-filtered workflow registration" "release.yml not found"
    fi
else
    check_skip "Path-filtered workflow registration" "no workflow files changed"
fi

# Check 15: Schedule trigger guards
# Ensures CI jobs have `if:` guards to exclude schedule events when the
# workflow has a schedule trigger. Without these guards, scheduled runs
# (e.g., daily security audits) execute all jobs unnecessarily.
echo "${BLUE}[15/${TOTAL_CHECKS}]${NC} Checking schedule trigger guards..."

if [ -n "$WORKFLOW_FILES" ]; then
    SCHED_ERRORS=0

    for wf_file in $WORKFLOW_FILES; do
        [ -f "$wf_file" ] || continue

        # Check if this workflow has a schedule trigger
        HAS_SCHEDULE=$(grep -c '^[[:space:]]*schedule:' "$wf_file" 2>/dev/null || echo "0")
        [ "$HAS_SCHEDULE" -eq 0 ] && continue

        # Check for workflow-level allowlist: if the workflow has a top-level
        # comment "# all-jobs-run-on-schedule" in the first 30 lines, all jobs
        # are intended to run on schedule, so skip the per-job check entirely.
        if head -30 "$wf_file" | grep -q '# all-jobs-run-on-schedule'; then
            continue
        fi

        # Extract job names and check for schedule guards
        # A job that should run on schedule won't have the guard (e.g., security audit).
        # Jobs that should NOT run on schedule need: if: github.event_name != 'schedule'
        # We look for jobs that have neither a schedule-aware `if:` guard
        # nor an explicit comment indicating they should run on schedule.
        #
        # Use AWK to find job blocks and check for if: guards
        JOBS_WITHOUT_GUARD=$(awk '
            /^jobs:/ { in_jobs = 1; next }
            !in_jobs { next }
            # Match top-level job key (2-space indent under jobs:)
            /^  [a-zA-Z_][a-zA-Z0-9_-]*:/ {
                if (job_name != "" && !has_guard && !is_schedule_job) {
                    print job_name
                }
                job_name = $1
                sub(/:$/, "", job_name)
                has_guard = 0
                is_schedule_job = 0
                next
            }
            # Check for schedule exclusion guard in job-level if:
            /^    if:.*schedule/ { has_guard = 1 }
            # Check for explicit per-job annotation or keyword heuristic
            /^    #.*runs-on-schedule/ { is_schedule_job = 1 }
            /^    #.*schedule|^    #.*security|^    #.*audit|^    #.*daily/ { is_schedule_job = 1 }
            END {
                if (job_name != "" && !has_guard && !is_schedule_job) {
                    print job_name
                }
            }
        ' "$wf_file")

        if [ -n "$JOBS_WITHOUT_GUARD" ]; then
            for job_name in $JOBS_WITHOUT_GUARD; do
                echo "${RED}  Missing guard:${NC} ${wf_file}: job '${job_name}' has no schedule exclusion guard"
                echo "${RED}    Add: if: github.event_name != 'schedule'${NC}"
                SCHED_ERRORS=$((SCHED_ERRORS + 1))
            done
        fi
    done

    if [ "$SCHED_ERRORS" -gt 0 ]; then
        check_fail "Schedule trigger guards" "Jobs in schedule-triggered workflows need 'if: github.event_name != '\"'\"'schedule'\"'\"'' guards."
        echo ""
    else
        check_pass "Schedule trigger guards"
    fi
else
    check_skip "Schedule trigger guards" "no workflow files changed"
fi

# Check 16: Skills index generation freshness
# Ensures .llm/skills/index.md is generated and up-to-date when staged inputs
# change (added/modified/deleted/renamed): skill files, generator script,
# or .llm/context.md.
echo "${BLUE}[16/${TOTAL_CHECKS}]${NC} Checking generated skills index freshness..."
STAGED_SKILLS_INDEX_BYTES=$(git diff --cached --name-only -z --diff-filter=ACDMR -- \
    .llm/context.md \
    scripts/generate-skills-index.sh \
    ':(glob).llm/skills/*.md' \
    | wc -c | tr -d '[:space:]')

if [ "$STAGED_SKILLS_INDEX_BYTES" -gt 0 ]; then
    if [ -x scripts/generate-skills-index.sh ]; then
        if scripts/generate-skills-index.sh --check > /dev/null 2>&1; then
            check_pass "Skills index freshness"
        else
            check_fail "Skills index freshness" "Run './scripts/generate-skills-index.sh' and stage .llm/skills/index.md."
            echo ""
        fi
    else
        check_fail "Skills index freshness" "scripts/generate-skills-index.sh not found or not executable."
        echo ""
    fi
else
    check_skip "Skills index freshness" "no .llm skills/context/index-related files staged"
fi

# Check 17: Workflow hygiene script checks
# Runs the comprehensive workflow-hygiene validator when workflow definitions
# or the validator script itself is staged.
echo "${BLUE}[17/${TOTAL_CHECKS}]${NC} Running workflow hygiene script checks..."
STAGED_WORKFLOW_HYGIENE_BYTES=$(git diff --cached --name-only -z --diff-filter=ACDMR -- \
    ':(glob).github/workflows/*.yml' \
    ':(glob).github/workflows/*.yaml' \
    scripts/check-workflow-hygiene.sh \
    .github/workflows/workflow-hygiene.yml \
    | wc -c | tr -d '[:space:]')

if [ "$STAGED_WORKFLOW_HYGIENE_BYTES" -gt 0 ]; then
    if [ -x scripts/check-workflow-hygiene.sh ]; then
        if scripts/check-workflow-hygiene.sh > /dev/null 2>&1; then
            check_pass "Workflow hygiene checks"
        else
            check_fail "Workflow hygiene checks" "Run './scripts/check-workflow-hygiene.sh' and fix reported errors."
            echo ""
        fi
    else
        check_fail "Workflow hygiene checks" "scripts/check-workflow-hygiene.sh not found or not executable."
        echo ""
    fi
else
    check_skip "Workflow hygiene checks" "no workflow or hygiene-script files staged"
fi

# Check 18: LLM file size limit (only if .llm/ files are staged)
# Ensures all files in .llm/ stay within the 300-line limit so they remain
# focused, scannable, and fit within LLM context windows.
echo "${BLUE}[18/${TOTAL_CHECKS}]${NC} Checking LLM file sizes..."
STAGED_LLM_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '^\.llm/.*\.md$' || true)

if [ -n "$STAGED_LLM_FILES" ]; then
    if [ -x scripts/check-llm-file-sizes.sh ]; then
        if ! scripts/check-llm-file-sizes.sh --files $STAGED_LLM_FILES > /dev/null 2>&1; then
            check_fail "LLM file sizes" "One or more .llm/ files exceed 300 lines. Run './scripts/check-llm-file-sizes.sh' for details."
            echo "${YELLOW}Tip:${NC} Split oversized files into focused sub-files of ≤300 lines."
            echo ""
        else
            check_pass "LLM file sizes"
        fi
    else
        check_skip "LLM file sizes" "check-llm-file-sizes.sh not found or not executable"
    fi
else
    check_skip "LLM file sizes" "no .llm/*.md files staged"
fi

# Check 19: Miri compatibility (only if Rust source files staged)
# Ensures lib tests calling Miri-incompatible APIs have #[cfg_attr(miri, ignore)].
echo "${BLUE}[19/${TOTAL_CHECKS}]${NC} Checking Miri compatibility..."
STAGED_SRC_RS=$(git diff --cached --name-only --diff-filter=ACM | grep -E '^src/.*\.rs$' || true)

if [ -n "$STAGED_SRC_RS" ]; then
    if [ -x scripts/check-miri-compat.sh ]; then
        MIRI_OUTPUT=$(scripts/check-miri-compat.sh 2>&1)
        if echo "$MIRI_OUTPUT" | grep -q 'WARNING:'; then
            check_warn "Miri compatibility" "Some lib tests may need #[cfg_attr(miri, ignore)]. Run './scripts/check-miri-compat.sh' for details."
        else
            check_pass "Miri compatibility"
        fi
    else
        check_skip "Miri compatibility" "check-miri-compat.sh not found or not executable"
    fi
else
    check_skip "Miri compatibility" "no src/*.rs files staged"
fi

# Summary
echo ""
echo "=========================================="
if [ "$CHECKS_FAILED" -gt 0 ]; then
    echo "${RED}[pre-commit] ${CHECKS_FAILED} check(s) FAILED, ${CHECKS_PASSED} passed${NC}"
    echo ""
    echo "${YELLOW}Fix the errors above before committing.${NC}"
    echo ""
    echo "${YELLOW}Quick fixes:${NC}"
    echo "  cargo fmt                                    # Fix formatting"
    echo "  cargo clippy --fix --allow-dirty            # Fix clippy warnings"
    echo "  ./scripts/check-markdown.sh fix             # Fix markdown"
    echo "  ./scripts/validate-ci.sh                    # Validate CI config"
    echo "  ./scripts/generate-skills-index.sh          # Refresh .llm/skills/index.md"
    echo "  ./scripts/check-llm-file-sizes.sh           # Check .llm/ file sizes"
    echo "  ./scripts/check-miri-compat.sh              # Check Miri annotations"
    echo "  ./scripts/run-local-ci.sh --fix             # Fix all issues"
    echo ""
    echo "${YELLOW}To bypass (NOT recommended):${NC}"
    echo "  git commit --no-verify"
    echo ""
    exit 1
else
    echo "${GREEN}[pre-commit] All checks passed! (${CHECKS_PASSED} passed)${NC}"
    echo ""
    exit 0
fi
