#!/bin/bash
# Pre-push hook: full verification including security checks
# Skip with: git push --no-verify
#
# This hook is contextual - it only runs checks for file types in the commits being pushed.

set -e

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m' # No Color

# Get the commits being pushed
CURRENT_BRANCH=$(git symbolic-ref --short HEAD)
UPSTREAM=$(git rev-parse --abbrev-ref @{u} 2>/dev/null || echo "")

if [ -z "$UPSTREAM" ]; then
    # No upstream, compare against main/master
    BASE_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo "main")
    CHANGED_FILES=$(git diff --name-only "origin/${BASE_BRANCH}...HEAD" 2>/dev/null || git diff --name-only HEAD~10..HEAD 2>/dev/null || echo "")
else
    # Compare against upstream
    CHANGED_FILES=$(git diff --name-only @{u}..HEAD 2>/dev/null || echo "")
fi

# Detect which file types are in the commits being pushed
HAS_GO_FILES=$(echo "$CHANGED_FILES" | grep -E '\.go$' || true)
HAS_MD_FILES=$(echo "$CHANGED_FILES" | grep -E '\.md$' || true)
HAS_TS_FILES=$(echo "$CHANGED_FILES" | grep -E '\.(ts|tsx)$' || true)

# Exit early if no relevant files in the push
if [ -z "$HAS_GO_FILES" ] && [ -z "$HAS_MD_FILES" ] && [ -z "$HAS_TS_FILES" ]; then
    echo -e "${GREEN}✓${NC} No Go, Markdown, or TypeScript files in push, skipping checks"
    exit 0
fi

# Header
echo ""
echo -e "${BLUE}╔═══════════════════════════════════════════════════╗${NC}"
echo -e "${BLUE}║${NC}  ${BOLD}PRE-PUSH VERIFICATION${NC}                            ${BLUE}║${NC}"
echo -e "${BLUE}╚═══════════════════════════════════════════════════╝${NC}"
echo ""

# Show what's being pushed
echo -e "${YELLOW}Branch:${NC} ${CURRENT_BRANCH}"
echo -e "${YELLOW}Commits:${NC}"
if [ -n "$UPSTREAM" ]; then
    git log --oneline @{u}.. 2>/dev/null | head -5 | sed 's/^/  /' || echo "  (new branch)"
else
    echo "  (new branch)"
fi
echo ""

# Show what's being checked
if [ -n "$HAS_GO_FILES" ]; then
    GO_COUNT=$(echo "$HAS_GO_FILES" | wc -l | tr -d ' ')
    echo -e "${YELLOW}Go files changed:${NC} ${GO_COUNT} file(s)"
fi
if [ -n "$HAS_MD_FILES" ]; then
    MD_COUNT=$(echo "$HAS_MD_FILES" | wc -l | tr -d ' ')
    echo -e "${YELLOW}Markdown files changed:${NC} ${MD_COUNT} file(s)"
fi
if [ -n "$HAS_TS_FILES" ]; then
    TS_COUNT=$(echo "$HAS_TS_FILES" | wc -l | tr -d ' ')
    echo -e "${YELLOW}TypeScript files changed:${NC} ${TS_COUNT} file(s)"
fi
echo ""

# Track if any check fails
FAILED=0
START_TIME=$(date +%s)

# Count total checks to run
TOTAL_CHECKS=0
if [ -n "$HAS_GO_FILES" ]; then
    TOTAL_CHECKS=$((TOTAL_CHECKS + 6))  # fmt-check, vet, lint-go, gosec, vulncheck, test-race
fi
if [ -n "$HAS_MD_FILES" ]; then
    TOTAL_CHECKS=$((TOTAL_CHECKS + 1))  # markdownlint
fi
if [ -n "$HAS_TS_FILES" ]; then
    TOTAL_CHECKS=$((TOTAL_CHECKS + 2))  # eslint + tsc
fi
CURRENT_CHECK=0

# Helper function to run a check with timing
run_check() {
    local name=$1
    local command=$2
    local start=$(date +%s)
    CURRENT_CHECK=$((CURRENT_CHECK + 1))

    echo -e "${CYAN}[${CURRENT_CHECK}/${TOTAL_CHECKS}]${NC} ${YELLOW}▶${NC} ${name}..."
    if eval "$command" > /tmp/hook-output.txt 2>&1; then
        local duration=$(($(date +%s) - start))
        echo -e "       ${GREEN}✓${NC} ${name} passed ${CYAN}(${duration}s)${NC}"
        return 0
    else
        echo -e "       ${RED}✗${NC} ${name} failed"
        echo ""
        cat /tmp/hook-output.txt
        echo ""
        return 1
    fi
}

# Go checks (only if .go files are in the push)
if [ -n "$HAS_GO_FILES" ]; then
    # 1. Format check
    run_check "Checking Go code formatting" "make fmt-check" || FAILED=1

    # 2. Static analysis (go vet)
    run_check "Running static analysis (go vet)" "make vet" || FAILED=1

    # 3. Go linting
    run_check "Running golangci-lint" "make lint-go" || FAILED=1

    # 4. Security scan (gosec)
    echo -e "${CYAN}[$((CURRENT_CHECK + 1))/${TOTAL_CHECKS}]${NC} ${YELLOW}▶${NC} Running security scan (gosec)..."
    CURRENT_CHECK=$((CURRENT_CHECK + 1))
    START=$(date +%s)
    if make gosec > /tmp/hook-output.txt 2>&1; then
        DURATION=$(($(date +%s) - START))
        ISSUES=$(grep -oE "Issues : [0-9]+" /tmp/hook-output.txt | grep -oE "[0-9]+" || echo "0")
        NOSEC=$(grep -oE "Nosec  : [0-9]+" /tmp/hook-output.txt | grep -oE "[0-9]+" || echo "0")
        echo -e "       ${GREEN}✓${NC} Security scan passed - ${ISSUES} issues, ${NOSEC} suppressed ${CYAN}(${DURATION}s)${NC}"
    else
        echo -e "       ${RED}✗${NC} Security scan failed"
        echo ""
        cat /tmp/hook-output.txt
        echo ""
        FAILED=1
    fi

    # 5. Vulnerability check (govulncheck)
    echo -e "${CYAN}[$((CURRENT_CHECK + 1))/${TOTAL_CHECKS}]${NC} ${YELLOW}▶${NC} Checking for vulnerabilities (govulncheck)..."
    CURRENT_CHECK=$((CURRENT_CHECK + 1))
    START=$(date +%s)
    if make vulncheck > /tmp/hook-output.txt 2>&1; then
        DURATION=$(($(date +%s) - START))
        if grep -q "No vulnerabilities found" /tmp/hook-output.txt; then
            echo -e "       ${GREEN}✓${NC} No vulnerabilities found ${CYAN}(${DURATION}s)${NC}"
        else
            echo -e "       ${GREEN}✓${NC} Vulnerability check completed ${CYAN}(${DURATION}s)${NC}"
        fi
    else
        echo -e "       ${RED}✗${NC} Vulnerability check failed"
        echo ""
        cat /tmp/hook-output.txt
        echo ""
        FAILED=1
    fi

    # 6. Tests with race detector and coverage
    echo -e "${CYAN}[$((CURRENT_CHECK + 1))/${TOTAL_CHECKS}]${NC} ${YELLOW}▶${NC} Running tests with race detector..."
    CURRENT_CHECK=$((CURRENT_CHECK + 1))
    START=$(date +%s)
    if make test-race > /tmp/hook-output.txt 2>&1; then
        DURATION=$(($(date +%s) - START))
        PASSED=$(grep -c "^ok" /tmp/hook-output.txt || echo "0")
        echo -e "       ${GREEN}✓${NC} All tests passed (${PASSED} packages) ${CYAN}(${DURATION}s)${NC}"
    else
        echo -e "       ${RED}✗${NC} Tests failed"
        cat /tmp/hook-output.txt
        FAILED=1
    fi
fi

# Markdown checks (only if .md files are in the push)
if [ -n "$HAS_MD_FILES" ]; then
    run_check "Running markdownlint" "make lint-md" || FAILED=1
fi

# TypeScript checks (only if .ts/.tsx files are in the push)
if [ -n "$HAS_TS_FILES" ]; then
    run_check "Running ESLint (TypeScript)" "(cd web && pnpm lint)" || FAILED=1
    run_check "TypeScript type check (tsc --noEmit)" "make tsc" || FAILED=1
fi

# Summary
TOTAL_TIME=$(($(date +%s) - START_TIME))
echo ""
echo -e "${BLUE}╔═══════════════════════════════════════════════════╗${NC}"
if [ $FAILED -eq 0 ]; then
    echo -e "${BLUE}║${NC}  ${GREEN}${BOLD}✓ ALL CHECKS PASSED!${NC}                             ${BLUE}║${NC}"
    printf "${BLUE}║${NC}  ${CYAN}Total time: %-37s${NC}${BLUE}║${NC}\n" "${TOTAL_TIME}s"
    echo -e "${BLUE}╚═══════════════════════════════════════════════════╝${NC}"
    echo ""
    echo -e "${GREEN}Ready to push!${NC}"
    echo ""
    exit 0
else
    echo -e "${BLUE}║${NC}  ${RED}${BOLD}✗ CHECKS FAILED${NC}                                  ${BLUE}║${NC}"
    printf "${BLUE}║${NC}  ${CYAN}Total time: %-37s${NC}${BLUE}║${NC}\n" "${TOTAL_TIME}s"
    echo -e "${BLUE}╚═══════════════════════════════════════════════════╝${NC}"
    echo ""
    echo -e "${RED}Fix the issues above before pushing.${NC}"
    echo -e "${YELLOW}Or skip with: ${NC}git push --no-verify"
    echo ""
    exit 1
fi
