#!/usr/bin/env bash
# Rustberg pre-commit/pre-push checks
# Run this locally before pushing to catch CI failures early
#
# Usage:
#   ./scripts/check.sh              # Run all checks
#   ./scripts/check.sh --quick      # Run only fast checks (fmt, clippy, check)
#   ./scripts/check.sh --test       # Run checks + unit tests
#   ./scripts/check.sh --integration # Run checks + all tests including integration
#   ./scripts/check.sh --release    # Run full pre-release validation
#   ./scripts/check.sh --clean      # Clean build artifacts to free disk space
#   ./scripts/check.sh --help       # Show this help message
#
# To install as pre-commit hook:
#   ln -sf ../../scripts/check.sh .git/hooks/pre-commit

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
cd "$ROOT_DIR"

# Colors
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

# Timing
START_TIME=$(date +%s)
CHECKS_PASSED=0
CHECKS_FAILED=0
CHECKS_SKIPPED=0
WARNINGS=()

show_help() {
    echo -e "${BOLD}Rustberg CI Check Script${NC}"
    echo ""
    echo -e "${CYAN}Usage:${NC}"
    echo "  ./scripts/check.sh [OPTIONS]"
    echo ""
    echo -e "${CYAN}Options:${NC}"
    echo "  --quick, -q        Run only fast checks (fmt, clippy, check)"
    echo "  --test, -t         Run checks + unit tests"
    echo "  --integration, -i  Run checks + all tests including integration"
    echo "  --release, -r      Run full pre-release validation"
    echo "  --clean, -c        Clean build artifacts to free disk space"
    echo "  --verbose, -v      Show verbose output"
    echo "  --no-color         Disable colored output"
    echo "  --help, -h         Show this help message"
    echo ""
    echo -e "${CYAN}Examples:${NC}"
    echo "  ./scripts/check.sh              # Standard checks"
    echo "  ./scripts/check.sh -q           # Quick pre-commit check"
    echo "  ./scripts/check.sh -t           # Run with unit tests"
    echo "  ./scripts/check.sh -r           # Full release validation"
    echo "  ./scripts/check.sh -c           # Clean target folder"
    echo ""
    echo -e "${CYAN}Pre-commit Hook:${NC}"
    echo "  ln -sf ../../scripts/check.sh .git/hooks/pre-commit"
    echo ""
    exit 0
}

# Parse arguments
QUICK=false
TEST=false
INTEGRATION=false
RELEASE=false
CLEAN=false
VERBOSE=false
NO_COLOR=false
while [[ $# -gt 0 ]]; do
    case $1 in
        --quick|-q) QUICK=true; shift ;;
        --test|-t) TEST=true; shift ;;
        --integration|-i) INTEGRATION=true; shift ;;
        --release|-r) RELEASE=true; TEST=true; INTEGRATION=true; shift ;;
        --clean|-c) CLEAN=true; shift ;;
        --verbose|-v) VERBOSE=true; shift ;;
        --no-color) NO_COLOR=true; shift ;;
        --help|-h) show_help ;;
        *) echo "Unknown option: $1. Use --help for usage."; exit 1 ;;
    esac
done

# Disable colors if requested or not a TTY
if $NO_COLOR || [[ ! -t 1 ]]; then
    RED='' GREEN='' YELLOW='' BLUE='' CYAN='' BOLD='' NC=''
fi

print_step() {
    echo -e "${YELLOW}▶ $1${NC}"
}

print_ok() {
    echo -e "${GREEN}✓ $1${NC}"
    ((CHECKS_PASSED++)) || true
}

print_err() {
    echo -e "${RED}✗ $1${NC}"
    ((CHECKS_FAILED++)) || true
}

print_info() {
    echo -e "${BLUE}ℹ $1${NC}"
}

print_warn() {
    echo -e "${YELLOW}⚠ $1${NC}"
    WARNINGS+=("$1")
}

print_skip() {
    echo -e "${CYAN}⊘ $1 (skipped)${NC}"
    ((CHECKS_SKIPPED++)) || true
}

run_check() {
    local name="$1"
    shift
    print_step "$name"
    local start=$(date +%s)
    if "$@"; then
        local elapsed=$(($(date +%s) - start))
        if $VERBOSE; then
            print_ok "$name passed (${elapsed}s)"
        else
            print_ok "$name passed"
        fi
    else
        print_err "$name failed"
        exit 1
    fi
}

run_check_optional() {
    local name="$1"
    shift
    print_step "$name"
    if "$@"; then
        print_ok "$name passed"
    else
        print_warn "$name failed (non-blocking)"
    fi
}

print_section() {
    echo ""
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    echo -e "  ${BOLD}$1${NC}"
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
}

# ============================================================================
# Clean build artifacts
# ============================================================================
do_clean() {
    print_section "Cleaning Build Artifacts"

    local target_dir="$ROOT_DIR/target"

    if [[ -d "$target_dir" ]]; then
        local size_before
        size_before=$(du -sh "$target_dir" 2>/dev/null | cut -f1)
        print_info "Current target folder size: $size_before"

        print_step "Running cargo clean"
        if cargo clean; then
            print_ok "Build artifacts cleaned successfully"
            print_info "Freed approximately $size_before of disk space"
        else
            print_err "Failed to clean build artifacts"
            exit 1
        fi
    else
        print_info "Target directory does not exist — nothing to clean"
    fi

    # Hint about deeper cache cleaning
    if [[ -d "$ROOT_DIR/.cargo" ]]; then
        print_step "Cleaning .cargo cache (optional)"
        print_info "Run 'cargo cache --autoclean' for more thorough cleanup (requires cargo-cache)"
    fi

    echo ""
    print_ok "Cleanup complete!"
    exit 0
}

# Handle --clean flag early
if $CLEAN; then
    do_clean
fi

# ============================================================================
# Informational checks (non-blocking)
# ============================================================================
check_git_status() {
    print_step "Git Status"
    if [[ -d ".git" ]]; then
        local uncommitted
        uncommitted=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')
        if [[ "$uncommitted" -gt 0 ]]; then
            print_warn "You have $uncommitted uncommitted changes"
            if $VERBOSE; then
                git status --short
            fi
        else
            print_ok "Working directory clean"
        fi
    else
        print_skip "Git Status (not a git repository)"
    fi
}

check_cargo_lock() {
    print_step "Cargo.lock Freshness"
    if cargo check --locked --quiet 2>/dev/null; then
        print_ok "Cargo.lock is up to date"
    else
        print_warn "Cargo.lock may need updating (run 'cargo update')"
    fi
}

# ============================================================================
# Main
# ============================================================================
print_section "Rustberg CI Checks"

# Show environment info
print_info "Rust: $(rustc --version 2>/dev/null || echo 'not found')"
print_info "Cargo: $(cargo --version 2>/dev/null || echo 'not found')"

# Git status check (informational)
check_git_status

# Essential checks (always run)
run_check "Formatting" cargo fmt --all -- --check
run_check "Clippy" cargo clippy --all-targets --all-features -- -D warnings
run_check "Compilation" cargo check --all-features

# Cargo.lock freshness
check_cargo_lock

if $QUICK; then
    echo ""
    ELAPSED=$(($(date +%s) - START_TIME))
    print_ok "Quick checks passed! (${ELAPSED}s)"
    exit 0
fi

# Documentation build
print_step "Documentation"
if cargo doc --no-deps --all-features 2>&1 | grep -E "^error|warning:" | head -20; then
    print_warn "Documentation has warnings"
else
    print_ok "Documentation passed"
fi

# MSRV check (requires the toolchain to be installed)
MSRV="1.89"
if rustup run "$MSRV" cargo --version &>/dev/null; then
    run_check "MSRV ($MSRV)" rustup run "$MSRV" cargo check --all-features
else
    print_skip "MSRV ($MSRV) — toolchain not installed"
    print_info "Install with: rustup toolchain install $MSRV"
fi

# ============================================================================
# Unit tests
# ============================================================================
if $TEST; then
    print_section "Running Unit Tests"

    run_check "Library Tests" cargo test --all-features --lib
    run_check "Doc Tests" cargo test --all-features --doc
fi

# ============================================================================
# Integration tests
# ============================================================================
if $INTEGRATION; then
    print_section "Running Integration Tests"

    if command -v docker &>/dev/null && docker info &>/dev/null; then
        run_check "Integration Tests" cargo test --all-features -- --test-threads=1
    else
        print_err "Docker not available — integration tests require Docker"
        exit 1
    fi
fi

# ============================================================================
# Pre-release validation
# ============================================================================
if $RELEASE; then
    print_section "Pre-Release Validation"

    # Blocking: uncommitted changes
    if [[ -d ".git" ]]; then
        local_changes=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')
        if [[ "$local_changes" -gt 0 ]]; then
            print_warn "Uncommitted changes detected — consider committing before release"
        fi
    fi

    # Branch check
    print_step "Branch Check"
    CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
    if [[ "$CURRENT_BRANCH" == "main" || "$CURRENT_BRANCH" == "master" ]]; then
        print_ok "On release branch: $CURRENT_BRANCH"
    else
        print_warn "Not on main branch (currently on: $CURRENT_BRANCH)"
    fi

    # ── Security audit ────────────────────────────────────────────────
    if command -v cargo-deny &>/dev/null; then
        run_check_optional "cargo-deny" cargo deny check
    elif command -v cargo-audit &>/dev/null; then
        run_check_optional "Security Audit" cargo audit
    else
        print_skip "Security Audit — neither cargo-deny nor cargo-audit installed"
        print_info "Install with: cargo install cargo-deny"
    fi

    # ── Outdated dependencies ─────────────────────────────────────────
    if command -v cargo-outdated &>/dev/null; then
        print_step "Dependency Freshness"
        OUTDATED=$(cargo outdated --depth 1 2>/dev/null | grep -c "^[a-z]" || echo "0")
        if [[ "$OUTDATED" -gt 10 ]]; then
            print_warn "$OUTDATED direct dependencies have updates available"
        else
            print_ok "Dependencies reasonably up to date ($OUTDATED with updates)"
        fi
    else
        print_skip "Dependency Freshness — cargo-outdated not installed"
    fi

    # ── Duplicate dependencies ────────────────────────────────────────
    print_step "Duplicate Dependencies"
    DUPES=$(cargo tree --duplicates 2>/dev/null | grep -cE "^[a-z]" || echo "0")
    if [[ "$DUPES" -gt 20 ]]; then
        print_warn "Found $DUPES duplicate dependency versions (consider deduplication)"
    else
        print_ok "Duplicate dependencies within limits ($DUPES)"
    fi

    # ── Unused dependencies ───────────────────────────────────────────
    if command -v cargo-machete &>/dev/null; then
        run_check_optional "Unused Dependencies" cargo machete
    else
        print_skip "Unused Dependencies — cargo-machete not installed"
    fi

    # ── Version / tag checks ──────────────────────────────────────────
    print_step "Version Consistency"
    WORKSPACE_VERSION=$(grep -E "^version = " Cargo.toml | head -1 | cut -d'"' -f2)
    print_info "Package version: $WORKSPACE_VERSION"

    if [[ ! "$WORKSPACE_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
        print_warn "Version '$WORKSPACE_VERSION' may not be valid semver"
    else
        print_ok "Version format valid ($WORKSPACE_VERSION)"
    fi

    print_step "Git Tag Check"
    TAG_NAME="v$WORKSPACE_VERSION"
    if git rev-parse "$TAG_NAME" &>/dev/null; then
        print_warn "Git tag $TAG_NAME already exists"
    else
        print_ok "Git tag $TAG_NAME is available"
    fi

    # ── Release build ─────────────────────────────────────────────────
    print_section "Release Build"
    run_check "Release Build" cargo build --release --all-features

    # Binary size report
    print_step "Binary Size Report"
    BIN_PATH="target/release/rustberg"
    if [[ -f "$BIN_PATH" ]]; then
        SIZE_HUMAN=$(du -h "$BIN_PATH" | cut -f1)
        echo "    rustberg: $SIZE_HUMAN"
        print_ok "Binary Size Report complete"
    else
        print_warn "Release binary not found at $BIN_PATH"
    fi

    # ── Code quality analysis ─────────────────────────────────────────
    print_section "Code Quality Analysis"

    # TODO/FIXME grep
    print_step "TODO / FIXME Annotations"
    TODO_COUNT=$(grep -rn "TODO\|FIXME\|XXX\|HACK" --include="*.rs" src/ 2>/dev/null | wc -l | tr -d ' ' || echo "0")
    if [[ "$TODO_COUNT" -gt 0 ]]; then
        print_warn "Found $TODO_COUNT TODO/FIXME/XXX/HACK comments in src/"
        if $VERBOSE; then
            echo "  Top files with TODOs:"
            grep -rn "TODO\|FIXME\|XXX\|HACK" --include="*.rs" -l src/ 2>/dev/null | head -5 | while read -r f; do
                count=$(grep -c "TODO\|FIXME\|XXX\|HACK" "$f" 2>/dev/null || echo "0")
                echo "    $f: $count"
            done
        fi
    else
        print_ok "No TODO/FIXME comments found"
    fi

    # unwrap() / expect() stats
    UNWRAP_COUNT=$(grep -rn '\.unwrap()' --include="*.rs" src/ 2>/dev/null | wc -l | tr -d ' ' || echo "0")
    EXPECT_COUNT=$(grep -rn '\.expect(' --include="*.rs" src/ 2>/dev/null | wc -l | tr -d ' ' || echo "0")
    print_info "Found $UNWRAP_COUNT .unwrap() and $EXPECT_COUNT .expect() calls in src/"
    if [[ "$UNWRAP_COUNT" -gt 20 ]]; then
        print_warn "High unwrap() count ($UNWRAP_COUNT) — consider proper error handling"
    fi

    # unsafe code
    UNSAFE_COUNT=$(grep -rn 'unsafe ' --include="*.rs" src/ 2>/dev/null | grep -v '// *unsafe' | wc -l | tr -d ' ' || echo "0")
    if [[ "$UNSAFE_COUNT" -gt 0 ]]; then
        print_warn "Found $UNSAFE_COUNT unsafe blocks in src/"
    else
        print_ok "No unsafe code blocks found"
    fi

    # panic macros
    PANIC_COUNT=$(grep -rn 'panic!\|unreachable!\|unimplemented!\|todo!' --include="*.rs" src/ 2>/dev/null | wc -l | tr -d ' ' || echo "0")
    if [[ "$PANIC_COUNT" -gt 5 ]]; then
        print_warn "Found $PANIC_COUNT panic!/unreachable!/todo! macros in src/"
    else
        print_info "Found $PANIC_COUNT panic macros in src/ (acceptable)"
    fi

    # ── Feature matrix ────────────────────────────────────────────────
    print_section "Feature Matrix Validation"

    run_check "Build (no default features)" cargo check --no-default-features
    run_check "Build (slatedb-storage only)" cargo check --no-default-features --features slatedb-storage
    run_check "Build (tls only)" cargo check --no-default-features --features tls
    run_check "Build (all features)" cargo check --all-features

    # ── Helm chart ────────────────────────────────────────────────────
    if [[ -d "charts/rustberg" ]]; then
        print_section "Deployment Artifacts"

        if command -v helm &>/dev/null; then
            print_step "Helm Chart Validation"
            if helm lint charts/rustberg 2>/dev/null; then
                print_ok "Helm chart lint passed"
            else
                print_warn "Helm chart has lint warnings"
            fi

            if helm template test charts/rustberg >/dev/null 2>&1; then
                print_ok "Helm template renders successfully"
            else
                print_warn "Helm template rendering has issues"
            fi
        else
            print_skip "Helm Chart Validation — helm not installed"
        fi
    fi

    # ── Dockerfile ────────────────────────────────────────────────────
    if [[ -f "Dockerfile" ]]; then
        print_step "Dockerfile Validation"
        if command -v hadolint &>/dev/null; then
            if hadolint Dockerfile 2>/dev/null; then
                print_ok "Dockerfile passes hadolint"
            else
                print_warn "Dockerfile has lint warnings"
            fi
        else
            if grep -q "^FROM" Dockerfile; then
                print_ok "Dockerfile syntax appears valid"
            fi
        fi
    fi

    # ── Python integration tests config ───────────────────────────────
    if [[ -f "pyproject.toml" ]]; then
        print_step "Python Test Config"
        if command -v uv &>/dev/null; then
            print_ok "uv available for Python integration tests"
        else
            print_skip "Python Tests — uv not installed (install: curl -LsSf https://astral.sh/uv/install.sh | sh)"
        fi
    fi
fi

# ============================================================================
# Summary
# ============================================================================
print_section "Summary"

ELAPSED=$(($(date +%s) - START_TIME))
MINUTES=$((ELAPSED / 60))
SECONDS=$((ELAPSED % 60))

echo ""
echo -e "  ${GREEN}Passed:${NC}  $CHECKS_PASSED"
echo -e "  ${YELLOW}Skipped:${NC} $CHECKS_SKIPPED"
echo -e "  ${RED}Failed:${NC}  $CHECKS_FAILED"
echo -e "  ${BLUE}Time:${NC}    ${MINUTES}m ${SECONDS}s"
echo ""

if [[ ${#WARNINGS[@]} -gt 0 ]]; then
    echo -e "${YELLOW}Warnings:${NC}"
    for warn in "${WARNINGS[@]}"; do
        echo -e "  • $warn"
    done
    echo ""
fi

if [[ "$CHECKS_FAILED" -gt 0 ]]; then
    print_err "Some checks failed!"
    exit 1
fi

print_ok "All checks passed!"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
