#!/usr/bin/env bash
# =============================================================================
# test_helpers.sh — E2E Test Framework for ilum-cli
# =============================================================================
# Assertion framework, cleanup functions, logging, and summary reporting.
# Source this file from phase scripts and run_e2e.sh.
# =============================================================================

set -euo pipefail

# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------
export ILUM="${ILUM:-/home/ubuntu/test/ilum-helm/ilum-cli/.venv/bin/ilum}"
export HELM_RELEASE="${HELM_RELEASE:-ilum}"
export HELM_NAMESPACE="${HELM_NAMESPACE:-default}"
export ILUM_CHART_DIR="${ILUM_CHART_DIR:-/home/ubuntu/test/ilum-helm/helm_aio}"

# Test results directory — timestamped
export TEST_LOG_DIR="${TEST_LOG_DIR:-/home/ubuntu/test/e2e-results/$(date +%Y%m%d_%H%M%S)}"
mkdir -p "$TEST_LOG_DIR"

# ---------------------------------------------------------------------------
# Counters
# ---------------------------------------------------------------------------
TOTAL_TESTS=0
PASSED_TESTS=0
FAILED_TESTS=0
SKIPPED_TESTS=0

# Issue tracking
declare -a ISSUES=()
ISSUE_BUG_COUNT=0
ISSUE_UX_COUNT=0
ISSUE_SEC_COUNT=0
ISSUE_PERF_COUNT=0

# Failure details
declare -a FAILURES=()

# Current test context
CURRENT_TEST_ID=""
CURRENT_TEST_DESC=""

# ---------------------------------------------------------------------------
# Logging
# ---------------------------------------------------------------------------
_log() {
    local level="$1"; shift
    local msg="$*"
    local ts
    ts="$(date '+%Y-%m-%d %H:%M:%S')"
    echo "[$ts] [$level] $msg" | tee -a "$TEST_LOG_DIR/e2e.log"
}

log_info()  { _log "INFO"  "$@"; }
log_warn()  { _log "WARN"  "$@"; }
log_error() { _log "ERROR" "$@"; }
log_debug() {
    if [[ "${E2E_VERBOSE:-0}" == "1" ]]; then
        _log "DEBUG" "$@"
    fi
}

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

print_pass()  { echo -e "${GREEN}  PASS${NC} $1"; }
print_fail()  { echo -e "${RED}  FAIL${NC} $1"; }
print_skip()  { echo -e "${YELLOW}  SKIP${NC} $1"; }
print_phase() { echo -e "\n${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"; echo -e "${CYAN}  Phase: $1${NC}"; echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"; }

# ---------------------------------------------------------------------------
# Test execution
# ---------------------------------------------------------------------------

# run_test <test_id> <description> <command...>
#   Runs a command, captures stdout, stderr, exit code, duration.
#   Stores results in TEST_LOG_DIR/<test_id>/
#   Sets: LAST_EXIT_CODE, LAST_STDOUT, LAST_STDERR, LAST_DURATION
run_test() {
    local test_id="$1"; shift
    local desc="$1"; shift

    CURRENT_TEST_ID="$test_id"
    CURRENT_TEST_DESC="$desc"
    TOTAL_TESTS=$((TOTAL_TESTS + 1))

    local test_dir="$TEST_LOG_DIR/$test_id"
    mkdir -p "$test_dir"

    log_info "Running test $test_id: $desc"
    log_debug "Command: $*"

    local start_time end_time
    start_time=$(date +%s%N)

    # Run the command, capture everything
    set +e
    LAST_STDOUT=$("$@" 2>"$test_dir/stderr.txt")
    LAST_EXIT_CODE=$?
    set -e

    end_time=$(date +%s%N)
    LAST_DURATION=$(( (end_time - start_time) / 1000000 )) # ms

    LAST_STDERR=$(cat "$test_dir/stderr.txt" 2>/dev/null || true)

    # Save outputs
    echo "$LAST_STDOUT" > "$test_dir/stdout.txt"
    echo "$LAST_EXIT_CODE" > "$test_dir/exit_code.txt"
    echo "$LAST_DURATION" > "$test_dir/duration_ms.txt"
    echo "$desc" > "$test_dir/description.txt"
    echo "$*" > "$test_dir/command.txt"

    log_debug "Exit code: $LAST_EXIT_CODE, Duration: ${LAST_DURATION}ms"
}

# run_test_stdin <test_id> <description> <stdin_data> <command...>
#   Same as run_test but pipes stdin_data into the command.
run_test_stdin() {
    local test_id="$1"; shift
    local desc="$1"; shift
    local stdin_data="$1"; shift

    CURRENT_TEST_ID="$test_id"
    CURRENT_TEST_DESC="$desc"
    TOTAL_TESTS=$((TOTAL_TESTS + 1))

    local test_dir="$TEST_LOG_DIR/$test_id"
    mkdir -p "$test_dir"

    log_info "Running test $test_id: $desc"

    local start_time end_time
    start_time=$(date +%s%N)

    set +e
    LAST_STDOUT=$(echo "$stdin_data" | "$@" 2>"$test_dir/stderr.txt")
    LAST_EXIT_CODE=$?
    set -e

    end_time=$(date +%s%N)
    LAST_DURATION=$(( (end_time - start_time) / 1000000 ))

    LAST_STDERR=$(cat "$test_dir/stderr.txt" 2>/dev/null || true)

    echo "$LAST_STDOUT" > "$test_dir/stdout.txt"
    echo "$LAST_EXIT_CODE" > "$test_dir/exit_code.txt"
    echo "$LAST_DURATION" > "$test_dir/duration_ms.txt"
    echo "$desc" > "$test_dir/description.txt"
    echo "$*" > "$test_dir/command.txt"
}

# skip_test <test_id> <description> <reason>
skip_test() {
    local test_id="$1"
    local desc="$2"
    local reason="$3"
    TOTAL_TESTS=$((TOTAL_TESTS + 1))
    SKIPPED_TESTS=$((SKIPPED_TESTS + 1))
    print_skip "$test_id — $desc (Reason: $reason)"
    log_info "SKIP $test_id: $desc — $reason"
    mkdir -p "$TEST_LOG_DIR/$test_id"
    echo "SKIPPED: $reason" > "$TEST_LOG_DIR/$test_id/result.txt"
}

# ---------------------------------------------------------------------------
# Assertions — call after run_test
# ---------------------------------------------------------------------------

assert_exit_code() {
    local expected="${1:-0}"
    if [[ "$LAST_EXIT_CODE" -eq "$expected" ]]; then
        PASSED_TESTS=$((PASSED_TESTS + 1))
        print_pass "$CURRENT_TEST_ID — $CURRENT_TEST_DESC"
        echo "PASS" > "$TEST_LOG_DIR/$CURRENT_TEST_ID/result.txt"
        return 0
    else
        FAILED_TESTS=$((FAILED_TESTS + 1))
        print_fail "$CURRENT_TEST_ID — $CURRENT_TEST_DESC (expected exit $expected, got $LAST_EXIT_CODE)"
        echo "FAIL: expected exit $expected, got $LAST_EXIT_CODE" > "$TEST_LOG_DIR/$CURRENT_TEST_ID/result.txt"
        FAILURES+=("$CURRENT_TEST_ID — $CURRENT_TEST_DESC: expected exit $expected, got $LAST_EXIT_CODE")
        return 1
    fi
}

# assert_exit_code_not <code> — assert exit code is NOT this value
assert_exit_code_not() {
    local unexpected="$1"
    if [[ "$LAST_EXIT_CODE" -ne "$unexpected" ]]; then
        PASSED_TESTS=$((PASSED_TESTS + 1))
        print_pass "$CURRENT_TEST_ID — $CURRENT_TEST_DESC"
        echo "PASS" > "$TEST_LOG_DIR/$CURRENT_TEST_ID/result.txt"
        return 0
    else
        FAILED_TESTS=$((FAILED_TESTS + 1))
        print_fail "$CURRENT_TEST_ID — $CURRENT_TEST_DESC (exit code should not be $unexpected)"
        echo "FAIL: exit code should not be $unexpected" > "$TEST_LOG_DIR/$CURRENT_TEST_ID/result.txt"
        FAILURES+=("$CURRENT_TEST_ID — $CURRENT_TEST_DESC: exit code should not be $unexpected")
        return 1
    fi
}

assert_contains() {
    local expected="$1"
    local search_in="${2:-stdout}"
    local text
    if [[ "$search_in" == "stderr" ]]; then
        text="$LAST_STDERR"
    else
        text="$LAST_STDOUT"
    fi

    if echo "$text" | grep -qiF "$expected"; then
        PASSED_TESTS=$((PASSED_TESTS + 1))
        print_pass "$CURRENT_TEST_ID — $CURRENT_TEST_DESC (contains '$expected')"
        echo "PASS" > "$TEST_LOG_DIR/$CURRENT_TEST_ID/result.txt"
        return 0
    else
        FAILED_TESTS=$((FAILED_TESTS + 1))
        print_fail "$CURRENT_TEST_ID — $CURRENT_TEST_DESC ($search_in missing '$expected')"
        echo "FAIL: $search_in missing '$expected'" > "$TEST_LOG_DIR/$CURRENT_TEST_ID/result.txt"
        FAILURES+=("$CURRENT_TEST_ID — $CURRENT_TEST_DESC: $search_in missing '$expected'")
        return 1
    fi
}

assert_contains_regex() {
    local pattern="$1"
    local search_in="${2:-stdout}"
    local text
    if [[ "$search_in" == "stderr" ]]; then
        text="$LAST_STDERR"
    else
        text="$LAST_STDOUT"
    fi

    if echo "$text" | grep -qiE "$pattern"; then
        PASSED_TESTS=$((PASSED_TESTS + 1))
        print_pass "$CURRENT_TEST_ID — $CURRENT_TEST_DESC (matches '$pattern')"
        echo "PASS" > "$TEST_LOG_DIR/$CURRENT_TEST_ID/result.txt"
        return 0
    else
        FAILED_TESTS=$((FAILED_TESTS + 1))
        print_fail "$CURRENT_TEST_ID — $CURRENT_TEST_DESC ($search_in doesn't match '$pattern')"
        echo "FAIL: $search_in doesn't match '$pattern'" > "$TEST_LOG_DIR/$CURRENT_TEST_ID/result.txt"
        FAILURES+=("$CURRENT_TEST_ID — $CURRENT_TEST_DESC: $search_in doesn't match '$pattern'")
        return 1
    fi
}

assert_not_contains() {
    local unexpected="$1"
    local search_in="${2:-stdout}"
    local text
    if [[ "$search_in" == "stderr" ]]; then
        text="$LAST_STDERR"
    else
        text="$LAST_STDOUT"
    fi

    if echo "$text" | grep -qiF "$unexpected"; then
        FAILED_TESTS=$((FAILED_TESTS + 1))
        print_fail "$CURRENT_TEST_ID — $CURRENT_TEST_DESC ($search_in contains '$unexpected')"
        echo "FAIL: $search_in contains '$unexpected'" > "$TEST_LOG_DIR/$CURRENT_TEST_ID/result.txt"
        FAILURES+=("$CURRENT_TEST_ID — $CURRENT_TEST_DESC: $search_in unexpectedly contains '$unexpected'")
        return 1
    else
        PASSED_TESTS=$((PASSED_TESTS + 1))
        print_pass "$CURRENT_TEST_ID — $CURRENT_TEST_DESC (no '$unexpected')"
        echo "PASS" > "$TEST_LOG_DIR/$CURRENT_TEST_ID/result.txt"
        return 0
    fi
}

assert_json_valid() {
    if echo "$LAST_STDOUT" | python3 -m json.tool > /dev/null 2>&1; then
        PASSED_TESTS=$((PASSED_TESTS + 1))
        print_pass "$CURRENT_TEST_ID — $CURRENT_TEST_DESC (valid JSON)"
        echo "PASS" > "$TEST_LOG_DIR/$CURRENT_TEST_ID/result.txt"
        return 0
    else
        FAILED_TESTS=$((FAILED_TESTS + 1))
        print_fail "$CURRENT_TEST_ID — $CURRENT_TEST_DESC (invalid JSON output)"
        echo "FAIL: invalid JSON output" > "$TEST_LOG_DIR/$CURRENT_TEST_ID/result.txt"
        FAILURES+=("$CURRENT_TEST_ID — $CURRENT_TEST_DESC: invalid JSON output")
        return 1
    fi
}

assert_yaml_valid() {
    if echo "$LAST_STDOUT" | python3 -c "import sys,yaml; yaml.safe_load(sys.stdin)" > /dev/null 2>&1; then
        PASSED_TESTS=$((PASSED_TESTS + 1))
        print_pass "$CURRENT_TEST_ID — $CURRENT_TEST_DESC (valid YAML)"
        echo "PASS" > "$TEST_LOG_DIR/$CURRENT_TEST_ID/result.txt"
        return 0
    else
        FAILED_TESTS=$((FAILED_TESTS + 1))
        print_fail "$CURRENT_TEST_ID — $CURRENT_TEST_DESC (invalid YAML output)"
        echo "FAIL: invalid YAML output" > "$TEST_LOG_DIR/$CURRENT_TEST_ID/result.txt"
        FAILURES+=("$CURRENT_TEST_ID — $CURRENT_TEST_DESC: invalid YAML output")
        return 1
    fi
}

assert_duration_under() {
    local max_ms="$1"
    if [[ "$LAST_DURATION" -le "$max_ms" ]]; then
        PASSED_TESTS=$((PASSED_TESTS + 1))
        print_pass "$CURRENT_TEST_ID — $CURRENT_TEST_DESC (${LAST_DURATION}ms < ${max_ms}ms)"
        echo "PASS" > "$TEST_LOG_DIR/$CURRENT_TEST_ID/result.txt"
        return 0
    else
        FAILED_TESTS=$((FAILED_TESTS + 1))
        print_fail "$CURRENT_TEST_ID — $CURRENT_TEST_DESC (${LAST_DURATION}ms > ${max_ms}ms)"
        echo "FAIL: took ${LAST_DURATION}ms, limit ${max_ms}ms" > "$TEST_LOG_DIR/$CURRENT_TEST_ID/result.txt"
        FAILURES+=("$CURRENT_TEST_ID — $CURRENT_TEST_DESC: took ${LAST_DURATION}ms, limit ${max_ms}ms")
        return 1
    fi
}

assert_file_exists() {
    local path="$1"
    if [[ -f "$path" ]]; then
        PASSED_TESTS=$((PASSED_TESTS + 1))
        print_pass "$CURRENT_TEST_ID — $CURRENT_TEST_DESC (file exists: $path)"
        echo "PASS" > "$TEST_LOG_DIR/$CURRENT_TEST_ID/result.txt"
        return 0
    else
        FAILED_TESTS=$((FAILED_TESTS + 1))
        print_fail "$CURRENT_TEST_ID — $CURRENT_TEST_DESC (file missing: $path)"
        echo "FAIL: file missing: $path" > "$TEST_LOG_DIR/$CURRENT_TEST_ID/result.txt"
        FAILURES+=("$CURRENT_TEST_ID — $CURRENT_TEST_DESC: file missing: $path")
        return 1
    fi
}

assert_file_not_exists() {
    local path="$1"
    if [[ ! -f "$path" ]]; then
        PASSED_TESTS=$((PASSED_TESTS + 1))
        print_pass "$CURRENT_TEST_ID — $CURRENT_TEST_DESC (file absent: $path)"
        echo "PASS" > "$TEST_LOG_DIR/$CURRENT_TEST_ID/result.txt"
        return 0
    else
        FAILED_TESTS=$((FAILED_TESTS + 1))
        print_fail "$CURRENT_TEST_ID — $CURRENT_TEST_DESC (file should not exist: $path)"
        echo "FAIL: file should not exist: $path" > "$TEST_LOG_DIR/$CURRENT_TEST_ID/result.txt"
        FAILURES+=("$CURRENT_TEST_ID — $CURRENT_TEST_DESC: file should not exist: $path")
        return 1
    fi
}

assert_output_not_empty() {
    if [[ -n "$LAST_STDOUT" ]]; then
        PASSED_TESTS=$((PASSED_TESTS + 1))
        print_pass "$CURRENT_TEST_ID — $CURRENT_TEST_DESC (non-empty output)"
        echo "PASS" > "$TEST_LOG_DIR/$CURRENT_TEST_ID/result.txt"
        return 0
    else
        FAILED_TESTS=$((FAILED_TESTS + 1))
        print_fail "$CURRENT_TEST_ID — $CURRENT_TEST_DESC (empty output)"
        echo "FAIL: empty output" > "$TEST_LOG_DIR/$CURRENT_TEST_ID/result.txt"
        FAILURES+=("$CURRENT_TEST_ID — $CURRENT_TEST_DESC: empty output")
        return 1
    fi
}

# Compound assertion: assert exit code AND contains
assert_success_with() {
    local expected="$1"
    if [[ "$LAST_EXIT_CODE" -eq 0 ]] && echo "$LAST_STDOUT" | grep -qiF "$expected"; then
        PASSED_TESTS=$((PASSED_TESTS + 1))
        print_pass "$CURRENT_TEST_ID — $CURRENT_TEST_DESC"
        echo "PASS" > "$TEST_LOG_DIR/$CURRENT_TEST_ID/result.txt"
        return 0
    else
        FAILED_TESTS=$((FAILED_TESTS + 1))
        local reason=""
        [[ "$LAST_EXIT_CODE" -ne 0 ]] && reason="exit code=$LAST_EXIT_CODE"
        echo "$LAST_STDOUT" | grep -qiF "$expected" || reason="${reason:+$reason; }stdout missing '$expected'"
        print_fail "$CURRENT_TEST_ID — $CURRENT_TEST_DESC ($reason)"
        echo "FAIL: $reason" > "$TEST_LOG_DIR/$CURRENT_TEST_ID/result.txt"
        FAILURES+=("$CURRENT_TEST_ID — $CURRENT_TEST_DESC: $reason")
        return 1
    fi
}

# assert_fail_with <expected_exit_code> <expected_text>
assert_fail_with() {
    local expected_exit="$1"
    local expected_text="$2"
    local combined="$LAST_STDOUT$LAST_STDERR"

    if [[ "$LAST_EXIT_CODE" -eq "$expected_exit" ]] && echo "$combined" | grep -qiF "$expected_text"; then
        PASSED_TESTS=$((PASSED_TESTS + 1))
        print_pass "$CURRENT_TEST_ID — $CURRENT_TEST_DESC"
        echo "PASS" > "$TEST_LOG_DIR/$CURRENT_TEST_ID/result.txt"
        return 0
    else
        FAILED_TESTS=$((FAILED_TESTS + 1))
        local reason=""
        [[ "$LAST_EXIT_CODE" -ne "$expected_exit" ]] && reason="exit code=$LAST_EXIT_CODE (expected $expected_exit)"
        echo "$combined" | grep -qiF "$expected_text" || reason="${reason:+$reason; }output missing '$expected_text'"
        print_fail "$CURRENT_TEST_ID — $CURRENT_TEST_DESC ($reason)"
        echo "FAIL: $reason" > "$TEST_LOG_DIR/$CURRENT_TEST_ID/result.txt"
        FAILURES+=("$CURRENT_TEST_ID — $CURRENT_TEST_DESC: $reason")
        return 1
    fi
}

# ---------------------------------------------------------------------------
# Issue Tracking
# ---------------------------------------------------------------------------

# log_issue <category> <severity> <test_id> <description>
#   category: BUG, UX, SECURITY, PERF
#   severity: high, medium, low
log_issue() {
    local category="$1"
    local severity="$2"
    local test_id="$3"
    local description="$4"

    local prefix
    case "$category" in
        BUG)      prefix="BUG"; ISSUE_BUG_COUNT=$((ISSUE_BUG_COUNT + 1)); prefix="BUG-$(printf '%03d' $ISSUE_BUG_COUNT)" ;;
        UX)       prefix="UX";  ISSUE_UX_COUNT=$((ISSUE_UX_COUNT + 1));   prefix="UX-$(printf '%03d' $ISSUE_UX_COUNT)" ;;
        SECURITY) prefix="SEC"; ISSUE_SEC_COUNT=$((ISSUE_SEC_COUNT + 1)); prefix="SEC-$(printf '%03d' $ISSUE_SEC_COUNT)" ;;
        PERF)     prefix="PERF"; ISSUE_PERF_COUNT=$((ISSUE_PERF_COUNT + 1)); prefix="PERF-$(printf '%03d' $ISSUE_PERF_COUNT)" ;;
        *)        prefix="MISC" ;;
    esac

    local entry="$prefix | $severity | $test_id | $description"
    ISSUES+=("$entry")
    log_warn "ISSUE: $entry"

    # Append to ISSUES.md
    local issues_file="$TEST_LOG_DIR/ISSUES.md"
    if [[ ! -f "$issues_file" ]]; then
        cat > "$issues_file" <<'HEADER'
# E2E Issues Found

| ID | Severity | Test | Description |
|----|----------|------|-------------|
HEADER
    fi
    echo "| $prefix | $severity | $test_id | $description |" >> "$issues_file"
}

# ---------------------------------------------------------------------------
# Cleanup Functions
# ---------------------------------------------------------------------------

cleanup_config() {
    log_info "Cleaning ilum config directories"
    rm -rf "${HOME}/.config/ilum" 2>/dev/null || true
    rm -rf "${HOME}/.local/state/ilum" 2>/dev/null || true
    rm -rf "${HOME}/.local/share/ilum" 2>/dev/null || true
    rm -rf "${HOME}/.cache/ilum" 2>/dev/null || true
}

backup_config() {
    local backup_dir="$TEST_LOG_DIR/config_backup"
    mkdir -p "$backup_dir"
    log_info "Backing up config to $backup_dir"
    cp -r "${HOME}/.config/ilum" "$backup_dir/config" 2>/dev/null || true
    cp -r "${HOME}/.local/state/ilum" "$backup_dir/state" 2>/dev/null || true
    cp -r "${HOME}/.local/share/ilum" "$backup_dir/data" 2>/dev/null || true
    cp -r "${HOME}/.cache/ilum" "$backup_dir/cache" 2>/dev/null || true
}

restore_config() {
    local backup_dir="$TEST_LOG_DIR/config_backup"
    log_info "Restoring config from $backup_dir"
    cleanup_config
    [[ -d "$backup_dir/config" ]] && mkdir -p "${HOME}/.config" && cp -r "$backup_dir/config" "${HOME}/.config/ilum"
    [[ -d "$backup_dir/state" ]]  && mkdir -p "${HOME}/.local/state" && cp -r "$backup_dir/state" "${HOME}/.local/state/ilum"
    [[ -d "$backup_dir/data" ]]   && mkdir -p "${HOME}/.local/share" && cp -r "$backup_dir/data" "${HOME}/.local/share/ilum"
    [[ -d "$backup_dir/cache" ]]  && mkdir -p "${HOME}/.cache" && cp -r "$backup_dir/cache" "${HOME}/.cache/ilum"
}

cleanup_helm_release() {
    local release="${1:-$HELM_RELEASE}"
    local namespace="${2:-$HELM_NAMESPACE}"
    log_info "Cleaning up Helm release: $release in namespace $namespace"
    helm uninstall "$release" -n "$namespace" --wait 2>/dev/null || true
}

cleanup_pvcs() {
    local namespace="${1:-$HELM_NAMESPACE}"
    log_info "Cleaning PVCs in namespace $namespace"
    kubectl delete pvc --all -n "$namespace" --wait=false 2>/dev/null || true
}

cleanup_namespace() {
    local namespace="$1"
    if [[ "$namespace" == "default" || "$namespace" == "kube-system" ]]; then
        log_warn "Refusing to delete system namespace: $namespace"
        return 1
    fi
    log_info "Deleting namespace: $namespace"
    kubectl delete namespace "$namespace" --wait=false 2>/dev/null || true
}

wait_for_pods_ready() {
    local namespace="${1:-$HELM_NAMESPACE}"
    local timeout="${2:-300}"
    log_info "Waiting up to ${timeout}s for pods to be ready in namespace $namespace"

    local deadline=$(($(date +%s) + timeout))
    while [[ $(date +%s) -lt $deadline ]]; do
        local not_ready
        not_ready=$(kubectl get pods -n "$namespace" --no-headers 2>/dev/null | grep -v -E "Running|Completed|Succeeded" | wc -l)
        if [[ "$not_ready" -eq 0 ]]; then
            local total
            total=$(kubectl get pods -n "$namespace" --no-headers 2>/dev/null | wc -l)
            if [[ "$total" -gt 0 ]]; then
                log_info "All $total pods ready in namespace $namespace"
                return 0
            fi
        fi
        sleep 5
    done
    log_warn "Timeout waiting for pods in namespace $namespace"
    kubectl get pods -n "$namespace" 2>/dev/null || true
    return 1
}

wait_for_helm_release() {
    local release="${1:-$HELM_RELEASE}"
    local namespace="${2:-$HELM_NAMESPACE}"
    local timeout="${3:-600}"
    log_info "Waiting up to ${timeout}s for Helm release $release"

    local deadline=$(($(date +%s) + timeout))
    while [[ $(date +%s) -lt $deadline ]]; do
        local status
        status=$(helm status "$release" -n "$namespace" -o json 2>/dev/null | python3 -c "import sys,json; print(json.load(sys.stdin).get('info',{}).get('status',''))" 2>/dev/null || echo "")
        if [[ "$status" == "deployed" ]]; then
            log_info "Release $release is deployed"
            return 0
        fi
        sleep 10
    done
    log_warn "Timeout waiting for release $release"
    return 1
}

# ---------------------------------------------------------------------------
# Summary Report
# ---------------------------------------------------------------------------

print_summary() {
    echo ""
    echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}"
    echo -e "${CYAN}  E2E Test Results${NC}"
    echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}"
    echo ""
    echo -e "  Total:   ${BLUE}$TOTAL_TESTS${NC}"
    echo -e "  Passed:  ${GREEN}$PASSED_TESTS${NC}"
    echo -e "  Failed:  ${RED}$FAILED_TESTS${NC}"
    echo -e "  Skipped: ${YELLOW}$SKIPPED_TESTS${NC}"
    echo ""

    if [[ ${#FAILURES[@]} -gt 0 ]]; then
        echo -e "${RED}━━━ Failures ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
        for f in "${FAILURES[@]}"; do
            echo -e "  ${RED}✗${NC} $f"
        done
        echo ""
    fi

    if [[ ${#ISSUES[@]} -gt 0 ]]; then
        echo -e "${YELLOW}━━━ Issues Found ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
        for i in "${ISSUES[@]}"; do
            echo -e "  ${YELLOW}!${NC} $i"
        done
        echo ""
    fi

    # Save summary to file
    {
        echo "=== E2E Test Results ==="
        echo "Total: $TOTAL_TESTS | Passed: $PASSED_TESTS | Failed: $FAILED_TESTS | Skipped: $SKIPPED_TESTS"
        echo ""
        if [[ ${#FAILURES[@]} -gt 0 ]]; then
            echo "=== Failures ==="
            for f in "${FAILURES[@]}"; do
                echo "  $f"
            done
            echo ""
        fi
        if [[ ${#ISSUES[@]} -gt 0 ]]; then
            echo "=== Issues Found ==="
            for i in "${ISSUES[@]}"; do
                echo "  $i"
            done
        fi
    } > "$TEST_LOG_DIR/summary.txt"

    echo -e "  Results saved to: ${BLUE}$TEST_LOG_DIR${NC}"
    echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}"
    echo ""

    # Return failure if any tests failed
    [[ "$FAILED_TESTS" -eq 0 ]]
}

# ---------------------------------------------------------------------------
# Utility helpers
# ---------------------------------------------------------------------------

# Check if a command/binary exists
require_cmd() {
    local cmd="$1"
    if ! command -v "$cmd" &>/dev/null; then
        log_error "Required command not found: $cmd"
        return 1
    fi
}

# Get the combined stdout+stderr from last test
last_output() {
    echo "${LAST_STDOUT}${LAST_STDERR}"
}

# Check if the helm release exists
release_exists() {
    local release="${1:-$HELM_RELEASE}"
    local namespace="${2:-$HELM_NAMESPACE}"
    helm status "$release" -n "$namespace" &>/dev/null
}

# Get current helm revision
get_revision() {
    local release="${1:-$HELM_RELEASE}"
    local namespace="${2:-$HELM_NAMESPACE}"
    helm status "$release" -n "$namespace" -o json 2>/dev/null | python3 -c "import sys,json; print(json.load(sys.stdin).get('version', 0))" 2>/dev/null || echo "0"
}

# Create a temp values file
create_temp_values() {
    local content="$1"
    local tmpfile
    tmpfile=$(mktemp /tmp/ilum-e2e-values-XXXXXX.yaml)
    echo "$content" > "$tmpfile"
    echo "$tmpfile"
}
