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

# ─────────────────────────────────────────────────────────────────────────────
# picoclaw pre-commit hook
#
# Runs formatting, linting, build, and test checks on staged Go code.
# Install: make hooks
# Skip:    git commit --no-verify
# ─────────────────────────────────────────────────────────────────────────────

# ── Theme ────────────────────────────────────────────────────────────────────

BOLD='\033[1m'
DIM='\033[2m'
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'

PASS="${GREEN}✓${NC}"
FAIL="${RED}✗${NC}"
SKIP="${DIM}○${NC}"
SPIN_CHARS='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'

WIDTH=58
DIVIDER=$(printf '─%.0s' $(seq 1 $WIDTH))

# ── Helpers ──────────────────────────────────────────────────────────────────

elapsed() {
    local ms=$1
    if (( ms >= 1000 )); then
        printf "%d.%ds" $((ms / 1000)) $(( (ms % 1000) / 100 ))
    else
        printf "%dms" "$ms"
    fi
}

now_ms() {
    # Millisecond timestamp (POSIX-ish)
    if command -v date >/dev/null && date +%s%N >/dev/null 2>&1; then
        echo $(( $(date +%s%N) / 1000000 ))
    else
        echo $(( $(date +%s) * 1000 ))
    fi
}

# Print a step result line: icon  label  ...dots...  time
print_result() {
    local icon="$1" label="$2" time_str="$3"
    local pad_len=$(( WIDTH - ${#label} - ${#time_str} - 6 ))
    local dots=""
    if (( pad_len > 0 )); then
        dots=$(printf '.%.0s' $(seq 1 $pad_len))
    fi
    printf "  %b ${BOLD}%s${NC} ${DIM}%s${NC} %s\n" "$icon" "$label" "$dots" "$time_str"
}

run_step() {
    local label="$1"
    shift
    local start
    start=$(now_ms)

    local output
    local exit_code=0
    output=$("$@" 2>&1) || exit_code=$?

    local end
    end=$(now_ms)
    local dur=$(( end - start ))
    local time_str
    time_str=$(elapsed "$dur")

    if [[ $exit_code -eq 0 ]]; then
        print_result "$PASS" "$label" "$time_str"
        STEP_TIMES+=("$dur")
        return 0
    else
        print_result "$FAIL" "$label" "$time_str"
        STEP_TIMES+=("$dur")
        if [[ -n "$output" ]]; then
            echo ""
            printf "${DIM}%s${NC}\n" "$DIVIDER"
            echo "$output" | head -30
            local lines
            lines=$(echo "$output" | wc -l)
            if (( lines > 30 )); then
                printf "${DIM}  ... %d more lines (run manually to see full output)${NC}\n" $((lines - 30))
            fi
            printf "${DIM}%s${NC}\n" "$DIVIDER"
            echo ""
        fi
        return 1
    fi
}

# ── Detect staged Go files ───────────────────────────────────────────────────

STAGED_GO_FILES=$(git diff --cached --name-only --diff-filter=ACMR -- '*.go' || true)

if [[ -z "$STAGED_GO_FILES" ]]; then
    printf "\n  ${DIM}No staged .go files — skipping pre-commit checks.${NC}\n\n"
    exit 0
fi

STAGED_COUNT=$(echo "$STAGED_GO_FILES" | wc -l | tr -d ' ')
STAGED_PKGS=$(echo "$STAGED_GO_FILES" | xargs -I{} dirname {} | sort -u | sed 's|^|./|')

# ── Banner ───────────────────────────────────────────────────────────────────

TOTAL_START=$(now_ms)
STEP_TIMES=()
FAILED=0

echo ""
printf "  ${CYAN}${BOLD}picoclaw${NC} ${DIM}pre-commit${NC}  ${DIM}(%d file%s)${NC}\n" \
    "$STAGED_COUNT" "$([ "$STAGED_COUNT" -eq 1 ] && echo '' || echo 's')"
printf "  ${DIM}%s${NC}\n" "$DIVIDER"

# ── Step 1: Format ───────────────────────────────────────────────────────────

check_format() {
    local unformatted
    unformatted=$(gofmt -l $STAGED_GO_FILES 2>/dev/null || true)
    if [[ -n "$unformatted" ]]; then
        echo "Unformatted files (run 'go fmt ./...' or 'make fmt'):"
        echo "$unformatted" | sed 's/^/  /'
        return 1
    fi
}

run_step "Format" check_format || FAILED=1

# ── Step 2: Vet ─────────────────────────────────────────────────────────────

if [[ $FAILED -eq 0 ]]; then
    run_step "Lint (go vet)" go vet ./... || FAILED=1
else
    print_result "$SKIP" "Lint (go vet)" "skipped"
fi

# ── Step 3: Build ────────────────────────────────────────────────────────────

if [[ $FAILED -eq 0 ]]; then
    run_step "Build" go build ./... || FAILED=1
else
    print_result "$SKIP" "Build" "skipped"
fi

# ── Step 4: Test (staged packages only) ──────────────────────────────────────

is_sub_module() {
    local dir="${1#./}"
    local check="$dir"
    while [[ "$check" != "." && -n "$check" ]]; do
        if [[ -f "$check/go.mod" ]]; then
            return 0
        fi
        check=$(dirname "$check")
    done
    return 1
}

run_tests() {
    local pkgs="$STAGED_PKGS"
    if [[ -z "$pkgs" ]]; then
        pkgs="./..."
    fi

    local testable=""
    for pkg in $pkgs; do
        if is_sub_module "$pkg"; then
            continue
        fi
        if ls "${pkg}"/*_test.go >/dev/null 2>&1; then
            testable="$testable $pkg"
        fi
    done

    if [[ -z "$testable" ]]; then
        echo "(no test files in staged packages)"
        return 0
    fi

    go test -short -count=1 -timeout 60s $testable
}

if [[ $FAILED -eq 0 ]]; then
    run_step "Test" run_tests || FAILED=1
else
    print_result "$SKIP" "Test" "skipped"
fi

# ── Summary ──────────────────────────────────────────────────────────────────

TOTAL_END=$(now_ms)
TOTAL_DUR=$(( TOTAL_END - TOTAL_START ))
TOTAL_STR=$(elapsed "$TOTAL_DUR")

printf "  ${DIM}%s${NC}\n" "$DIVIDER"

if [[ $FAILED -eq 0 ]]; then
    printf "  ${GREEN}${BOLD}All checks passed${NC}  ${DIM}%s${NC}\n\n" "$TOTAL_STR"
    exit 0
else
    printf "  ${RED}${BOLD}Commit blocked${NC}  ${DIM}%s${NC}\n" "$TOTAL_STR"
    printf "  ${DIM}Fix errors above, or skip: ${NC}git commit --no-verify\n\n"
    exit 1
fi
