#!/bin/bash
# Pre-push hook for anno project
# COMPREHENSIVE checks (~30-60 seconds cold; faster with incremental builds)
# Goal: Ensure code quality before sharing with others
#
# Install: just setup-hooks
# Or: cp scripts/hooks/* .git/hooks/ && chmod +x .git/hooks/*
#
# Matches CI behavior for local/remote parity.
#
# Environment variables:
#   ANNO_SKIP_CLIPPY=1  Skip clippy check (useful when clippy is slow/hanging)
#                       Example: ANNO_SKIP_CLIPPY=1 git push
#   ANNO_SKIP_TESTS=1   Skip test check (useful when tests are slow/hanging)
#                       Example: ANNO_SKIP_TESTS=1 git push

set -e

# Track timing for performance monitoring
START_TIME=$(date +%s 2>/dev/null || echo 0)

echo "pre-push: running checks..."
echo ""

# Track warnings
WARNINGS=0

# Check for required tools
if ! command -v cargo >/dev/null 2>&1; then
    echo "error: cargo not found in PATH"
    echo "  Install Rust from: https://rustup.rs/"
    exit 1
fi

# ============================================================================
# 1. Format check (should be clean from pre-commit)
# ============================================================================
echo -n "  [1/5] format ....... "
if ! cargo fmt --all -- --check; then
    echo "FAILED"
    echo ""
    echo "error: code not formatted"
    echo "  run: cargo fmt --all"
    exit 1
fi
echo "ok"

# ============================================================================
# 2. Clippy with warnings as errors (lib only, tests have relaxed rules)
# ============================================================================
# IMPORTANT: Uses same feature flags as CI for parity
# Note: Only check lib code; test/bench code has relaxed clippy rules
# NOTE: Large generated files (dataset_registry.rs ~9.6k lines) can make clippy slow
# Skip clippy if ANNO_SKIP_CLIPPY=1 is set (useful when clippy is slow/hanging)
echo -n "  [2/5] clippy ....... "
if [ "${ANNO_SKIP_CLIPPY:-}" = "1" ]; then
    echo "SKIPPED (ANNO_SKIP_CLIPPY=1)"
    WARNINGS=$((WARNINGS + 1))
elif command -v timeout >/dev/null 2>&1; then
    # Use timeout if available (Linux/macOS with coreutils)
    # Reduced timeout to 60s - if it takes longer, it's likely stuck
    # Use --no-deps to skip dependency checking (faster) and limit to lib code
    # Capture output to temp file for better error handling
    CLIPPY_TMP=$(mktemp /tmp/clippy_output.XXXXXX)
    if timeout 60 cargo clippy --workspace --lib --features "eval discourse" --no-deps -- -D warnings > "$CLIPPY_TMP" 2>&1; then
        echo "ok"
        rm -f "$CLIPPY_TMP"
    else
        EXIT_CODE=$?
        CLIPPY_OUTPUT=$(cat "$CLIPPY_TMP" 2>/dev/null || echo "")
        rm -f "$CLIPPY_TMP"
        
        if [ $EXIT_CODE -eq 124 ]; then
            echo "TIMEOUT"
            echo ""
            echo "warning: clippy timed out after 60s (large files with eval can be slow)"
            echo "  To skip clippy: ANNO_SKIP_CLIPPY=1 git push"
            echo "  Or run manually: cargo clippy --workspace --lib --features 'eval discourse' -- -D warnings"
            WARNINGS=$((WARNINGS + 1))
        else
            echo "FAILED"
            echo ""
            echo "error: clippy found issues:"
            echo "$CLIPPY_OUTPUT" | head -50
            if [ $(echo "$CLIPPY_OUTPUT" | wc -l) -gt 50 ]; then
                echo ""
                echo "... (showing first 50 lines, run clippy manually for full output)"
            fi
            echo ""
            echo "  run: cargo clippy --workspace --lib --features 'eval discourse' -- -D warnings"
            exit 1
        fi
    fi
else
    # Fallback: no timeout (macOS without coreutils)
    # Skip clippy if no timeout available to avoid hanging
    echo "SKIPPED (no timeout command available)"
    echo "  Install coreutils for timeout support, or set ANNO_SKIP_CLIPPY=1"
    WARNINGS=$((WARNINGS + 1))
fi

# ============================================================================
# 3. Quick tests (blocking)
# ============================================================================
# Prefers cargo-nextest quick profile when available.
# NOTE (macOS): `cargo nextest` can be *very* slow on some setups due to Gatekeeper.
# If you're on macOS and pushes feel stuck during nextest "list" or startup, either:
# - add your terminal to Developer Tools exclusions (see nextest docs), or
# - set ANNO_USE_NEXTEST=1 to force nextest, otherwise we fall back to `cargo test`.
# Skip tests if ANNO_SKIP_TESTS=1 is set (useful when tests are slow/hanging)
echo -n "  [3/5] tests ........ "
if [ "${ANNO_SKIP_TESTS:-}" = "1" ]; then
    echo "SKIPPED (ANNO_SKIP_TESTS=1)"
    WARNINGS=$((WARNINGS + 1))
elif command -v timeout >/dev/null 2>&1; then
    # Use timeout to prevent hanging (quick profile should complete in <60s)
    OS_NAME="$(uname -s 2>/dev/null || echo unknown)"
    TEST_TMP=$(mktemp /tmp/test_output.XXXXXX)
    if command -v cargo-nextest >/dev/null 2>&1 && { [ "$OS_NAME" != "Darwin" ] || [ "${ANNO_USE_NEXTEST:-}" = "1" ]; }; then
        if timeout 120 cargo nextest run --profile quick --workspace --features "eval discourse" > "$TEST_TMP" 2>&1; then
            echo "ok"
            rm -f "$TEST_TMP"
        else
            EXIT_CODE=$?
            TEST_OUTPUT=$(cat "$TEST_TMP" 2>/dev/null || echo "")
            rm -f "$TEST_TMP"
            if [ $EXIT_CODE -eq 124 ]; then
                echo "TIMEOUT"
                echo ""
                echo "warning: tests timed out after 120s"
                echo "  To skip tests: ANNO_SKIP_TESTS=1 git push"
                echo "  Or run manually: cargo nextest run --profile quick --workspace --features 'eval discourse'"
                WARNINGS=$((WARNINGS + 1))
            else
                echo "FAILED"
                echo ""
                echo "error: tests failed:"
                echo "$TEST_OUTPUT" | tail -30
                echo ""
                echo "  run: cargo nextest run --profile quick --workspace --features 'eval discourse'"
                exit 1
            fi
        fi
    else
        if timeout 120 cargo test --workspace --lib --features "eval discourse" > "$TEST_TMP" 2>&1; then
            echo "ok"
            rm -f "$TEST_TMP"
        else
            EXIT_CODE=$?
            TEST_OUTPUT=$(cat "$TEST_TMP" 2>/dev/null || echo "")
            rm -f "$TEST_TMP"
            if [ $EXIT_CODE -eq 124 ]; then
                echo "TIMEOUT"
                echo ""
                echo "warning: tests timed out after 120s"
                echo "  To skip tests: ANNO_SKIP_TESTS=1 git push"
                echo "  Or run manually: cargo test --workspace --lib --features 'eval discourse'"
                WARNINGS=$((WARNINGS + 1))
            else
                echo "FAILED"
                echo ""
                echo "error: tests failed:"
                echo "$TEST_OUTPUT" | tail -30
                echo ""
                echo "  run: cargo test --workspace --lib --features 'eval discourse'"
                exit 1
            fi
        fi
    fi
else
    # Fallback: no timeout (macOS without coreutils)
    # Skip tests if no timeout available to avoid hanging
    echo "SKIPPED (no timeout command available)"
    echo "  Install coreutils for timeout support, or set ANNO_SKIP_TESTS=1"
    WARNINGS=$((WARNINGS + 1))
fi

# ============================================================================
# 4. Doc tests (skipped by default: ring/rustls doctests require full TLS toolchain)
# ============================================================================
echo "  [4/5] doc tests .... skipped (run manually if needed)"

# ============================================================================
# 5. Check for uncommitted changes
# ============================================================================
echo -n "  [5/5] clean state .. "
UNCOMMITTED=$(git status --porcelain | grep -v '^??' | wc -l | tr -d ' ')
if [ "$UNCOMMITTED" -gt 0 ]; then
    echo "warning"
    echo "        uncommitted changes:"
    git status --porcelain | grep -v '^??' | sed 's/^/        /'
    WARNINGS=$((WARNINGS + 1))
else
    echo "ok"
fi

# ============================================================================
# Summary
# ============================================================================
echo ""
END_TIME=$(date +%s 2>/dev/null || echo 0)
if [ $END_TIME -gt $START_TIME ]; then
    DURATION=$((END_TIME - START_TIME))
    if [ $DURATION -gt 60 ]; then
        MIN=$((DURATION / 60))
        SEC=$((DURATION % 60))
        TIME_STR="${MIN}m ${SEC}s"
    else
        TIME_STR="${DURATION}s"
    fi
else
    TIME_STR=""
fi

if [ $WARNINGS -gt 0 ]; then
    if [ -n "$TIME_STR" ]; then
        echo "pre-push: passed with $WARNINGS warning(s) (${TIME_STR})"
    else
        echo "pre-push: passed with $WARNINGS warning(s)"
    fi
    echo ""
    echo "Note: Some checks were skipped or had warnings. Review above for details."
    if command -v timeout >/dev/null 2>&1; then
        if [ "${ANNO_SKIP_CLIPPY:-}" != "1" ]; then
            echo "To skip clippy: ANNO_SKIP_CLIPPY=1 git push"
        fi
        if [ "${ANNO_SKIP_TESTS:-}" != "1" ]; then
            echo "To skip tests: ANNO_SKIP_TESTS=1 git push"
        fi
    fi
else
    if [ -n "$TIME_STR" ]; then
        echo "pre-push: all checks passed ✓ (${TIME_STR})"
    else
        echo "pre-push: all checks passed ✓"
    fi
fi
exit 0
