#!/bin/bash
# Elastic P2P stability test: runs 200-request benchmark with offload mode
# and baseline mode, then compares success rates.
#
# Must be run on dash0 (8 GPUs with Mooncake support).
#
# Usage:
#   bash scripts/run_elastic_stability_test.sh

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
VENV="${VENV_PATH:-$PROJECT_DIR/.venv/bin}"
PYTHON="$VENV/python"
VLLM="$VENV/vllm"

MODEL="${MODEL_PATH:-/home/admin/cpfs/wjh/models/Qwen/Qwen3-Coder-30B-A3B-Instruct}"
TRACE="$PROJECT_DIR/traces/sampled_1000req_seed42.jsonl"
N_INSTANCES=8
BASE_PORT=8000
PROXY_PORT=9090
REQUEST_LIMIT=200
TIME_SCALE=20
MAX_INFLIGHT=8
TIMESTAMP=$(date +%Y%m%d_%H%M%S)

OUT_ELASTIC="$PROJECT_DIR/outputs/elastic_stability_${TIMESTAMP}"
OUT_BASELINE="$PROJECT_DIR/outputs/baseline_stability_${TIMESTAMP}"

# ─── Helper functions ────────────────────────────────────────────────────────

kill_all() {
    echo "[cleanup] Killing vLLM and proxy processes..."
    for p in $(ps aux | grep 'vllm serve' | grep -v grep | awk '{print $2}' 2>/dev/null); do
        kill -9 "$p" 2>/dev/null || true
    done
    for p in $(ps aux | grep 'cache_aware_proxy' | grep -v grep | awk '{print $2}' 2>/dev/null); do
        kill -9 "$p" 2>/dev/null || true
    done
    sleep 5
    echo "[cleanup] Releasing GPUs..."
    for p in $(fuser /dev/nvidia* 2>/dev/null | tr ' ' '\n' | sort -u); do
        kill -9 "$p" 2>/dev/null || true
    done
    sleep 10
    echo "[cleanup] Done."
}

wait_for_instances() {
    local n=$1
    echo "[wait] Waiting for $n vLLM instances to become healthy..."
    for i in $(seq 0 $((n - 1))); do
        local port=$((BASE_PORT + i))
        local tries=0
        while ! curl -sf "http://127.0.0.1:$port/health" > /dev/null 2>&1; do
            tries=$((tries + 1))
            if [ $tries -ge 120 ]; then
                echo "[FAIL] Instance $i (port $port) did not start in 600s"
                return 1
            fi
            sleep 5
        done
        echo "  Instance $i (port $port) healthy"
    done
}

wait_for_bootstrap() {
    echo "[wait] Waiting for Mooncake bootstrap servers..."
    for i in $(seq 0 $((N_INSTANCES - 1))); do
        local bp=$((8998 + i))
        local tries=0
        while ! curl -sf "http://127.0.0.1:$bp/query" > /dev/null 2>&1; do
            tries=$((tries + 1))
            if [ $tries -ge 60 ]; then
                echo "[FAIL] Bootstrap $bp did not start in 120s"
                return 1
            fi
            sleep 2
        done
        echo "  Bootstrap $bp ready"
    done
}

wait_for_proxy() {
    echo "[wait] Waiting for proxy on port $PROXY_PORT..."
    local tries=0
    while ! curl -sf "http://127.0.0.1:$PROXY_PORT/stats" > /dev/null 2>&1; do
        tries=$((tries + 1))
        if [ $tries -ge 30 ]; then
            echo "[FAIL] Proxy did not start in 60s"
            return 1
        fi
        sleep 2
    done
    echo "  Proxy ready"
}

launch_vllm_kv_both() {
    echo ""
    echo "=== Launching $N_INSTANCES vLLM instances (kv_both) ==="
    for i in $(seq 0 $((N_INSTANCES - 1))); do
        local port=$((BASE_PORT + i))
        local bp=$((8998 + i))
        local master=$((29500 + i))
        local log="/tmp/elastic_test_${i}.log"

        VLLM_MOONCAKE_BOOTSTRAP_PORT=$bp \
        MASTER_PORT=$master \
        CUDA_VISIBLE_DEVICES=$i \
        $VLLM serve "$MODEL" \
            --host 0.0.0.0 --port "$port" --tensor-parallel-size 1 \
            --trust-remote-code --enable-prefix-caching --enforce-eager \
            --dtype auto --gpu-memory-utilization 0.9 --max-model-len 200000 \
            --kv-transfer-config '{"kv_connector":"MooncakeConnector","kv_role":"kv_both"}' \
            > "$log" 2>&1 &

        echo "  Instance $i: GPU=$i port=$port bootstrap=$bp log=$log"
        sleep 2
    done
    wait_for_instances $N_INSTANCES
    wait_for_bootstrap
}

launch_vllm_baseline() {
    echo ""
    echo "=== Launching $N_INSTANCES vLLM instances (baseline, no Mooncake) ==="
    for i in $(seq 0 $((N_INSTANCES - 1))); do
        local port=$((BASE_PORT + i))
        local master=$((29500 + i))
        local log="/tmp/baseline_test_${i}.log"

        MASTER_PORT=$master \
        CUDA_VISIBLE_DEVICES=$i \
        $VLLM serve "$MODEL" \
            --host 0.0.0.0 --port "$port" --tensor-parallel-size 1 \
            --trust-remote-code --enable-prefix-caching --enforce-eager \
            --dtype auto --gpu-memory-utilization 0.9 --max-model-len 200000 \
            > "$log" 2>&1 &

        echo "  Instance $i: GPU=$i port=$port log=$log"
        sleep 2
    done
    wait_for_instances $N_INSTANCES
}

launch_proxy_elastic() {
    echo ""
    echo "=== Starting proxy (elastic offload mode) ==="
    local combined_args=""
    local bp_list=""
    for i in $(seq 0 $((N_INSTANCES - 1))); do
        combined_args="$combined_args http://127.0.0.1:$((BASE_PORT + i))"
        bp_list="${bp_list:+$bp_list,}$((8998 + i))"
    done
    $PYTHON "$PROJECT_DIR/scripts/cache_aware_proxy.py" \
        --combined $combined_args \
        --bootstrap-ports "$bp_list" \
        --offload --heavy-threshold 20000 \
        --port $PROXY_PORT \
        > /tmp/proxy_elastic.log 2>&1 &
    wait_for_proxy
}

launch_proxy_baseline() {
    echo ""
    echo "=== Starting proxy (baseline, no offload) ==="
    local combined_args=""
    for i in $(seq 0 $((N_INSTANCES - 1))); do
        combined_args="$combined_args http://127.0.0.1:$((BASE_PORT + i))"
    done
    $PYTHON "$PROJECT_DIR/scripts/cache_aware_proxy.py" \
        --combined $combined_args \
        --port $PROXY_PORT \
        > /tmp/proxy_baseline.log 2>&1 &
    wait_for_proxy
}

run_benchmark() {
    local tag=$1
    local output_dir=$2
    mkdir -p "$output_dir"

    echo ""
    echo "=== Running benchmark: $tag ($REQUEST_LIMIT requests) ==="
    $PYTHON -m replayer \
        --trace "$TRACE" \
        --output "$output_dir/metrics.jsonl" \
        --endpoint "http://localhost:$PROXY_PORT" \
        --model "$MODEL" \
        --time-scale "$TIME_SCALE" \
        --max-inflight-sessions "$MAX_INFLIGHT" \
        --request-limit "$REQUEST_LIMIT" \
        -v 2>&1 | tee "$output_dir/replayer.log"

    # Save proxy breakdown and stats
    curl -sf "http://localhost:$PROXY_PORT/breakdown" > "$output_dir/breakdown.json" 2>/dev/null || true
    curl -sf "http://localhost:$PROXY_PORT/stats" > "$output_dir/stats.json" 2>/dev/null || true
}

collect_gpu_util() {
    local output_dir=$1
    nvidia-smi --query-gpu=index,utilization.gpu,memory.used,memory.total \
        --format=csv > "$output_dir/gpu_snapshot.csv" 2>/dev/null || true
}

print_summary() {
    local label=$1
    local output_dir=$2
    local metrics="$output_dir/metrics.jsonl"

    if [ ! -f "$metrics" ]; then
        echo "  [$label] No metrics file found!"
        return
    fi

    # Count total, success, error from metrics JSONL
    local total=$(wc -l < "$metrics")
    local success=$(grep -c '"error":null\|"error": null' "$metrics" 2>/dev/null || grep -c '"ttft":[0-9]' "$metrics" 2>/dev/null || echo 0)
    local errors=$((total - success))
    local rate="N/A"
    if [ "$total" -gt 0 ]; then
        rate=$(awk "BEGIN{printf \"%.1f\", ($success/$total)*100}")
    fi

    echo "  [$label]"
    echo "    Total requests: $total"
    echo "    Successful:     $success"
    echo "    Errors:         $errors"
    echo "    Success rate:   ${rate}%"

    # Print summary.json if it exists
    local summary="$output_dir/metrics.summary.json"
    if [ -f "$summary" ]; then
        echo "    Summary: $(cat "$summary")"
    fi
}

# ─── Main ────────────────────────────────────────────────────────────────────

echo "================================================================"
echo "  Elastic P2P Stability Test"
echo "  $(date)"
echo "  Model:    $MODEL"
echo "  Requests: $REQUEST_LIMIT"
echo "  Output:   elastic → $OUT_ELASTIC"
echo "            baseline → $OUT_BASELINE"
echo "================================================================"

# Sanity checks
if [ ! -f "$TRACE" ]; then
    echo "[ERROR] Trace file not found: $TRACE"
    exit 1
fi
if [ ! -x "$PYTHON" ]; then
    echo "[ERROR] Python not found: $PYTHON"
    exit 1
fi

# ─── Phase 1: Elastic P2P offload ────────────────────────────────────────────

echo ""
echo "############################################################"
echo "  Phase 1: Elastic P2P Offload"
echo "############################################################"

kill_all
launch_vllm_kv_both
launch_proxy_elastic
collect_gpu_util "$OUT_ELASTIC"
run_benchmark "elastic_p2p" "$OUT_ELASTIC"

echo ""
echo "[phase1] Saving APC stats..."
for i in $(seq 0 $((N_INSTANCES - 1))); do
    port=$((BASE_PORT + i))
    curl -sf "http://127.0.0.1:$port/metrics" 2>/dev/null \
        | grep -E 'vllm:cache_hit|prefix_cache' \
        >> "$OUT_ELASTIC/apc_metrics.txt" 2>/dev/null || true
done

# ─── Phase 2: Baseline (no offload) ─────────────────────────────────────────

echo ""
echo "############################################################"
echo "  Phase 2: Baseline (no offload, no Mooncake)"
echo "############################################################"

kill_all
launch_vllm_baseline
launch_proxy_baseline
collect_gpu_util "$OUT_BASELINE"
run_benchmark "baseline" "$OUT_BASELINE"

echo ""
echo "[phase2] Saving APC stats..."
for i in $(seq 0 $((N_INSTANCES - 1))); do
    port=$((BASE_PORT + i))
    curl -sf "http://127.0.0.1:$port/metrics" 2>/dev/null \
        | grep -E 'vllm:cache_hit|prefix_cache' \
        >> "$OUT_BASELINE/apc_metrics.txt" 2>/dev/null || true
done

# ─── Cleanup ─────────────────────────────────────────────────────────────────

kill_all

# ─── Comparison ──────────────────────────────────────────────────────────────

echo ""
echo "================================================================"
echo "  Results Comparison"
echo "================================================================"

print_summary "Elastic P2P" "$OUT_ELASTIC"
echo ""
print_summary "Baseline" "$OUT_BASELINE"

echo ""
echo "Detailed outputs:"
echo "  Elastic: $OUT_ELASTIC/"
echo "  Baseline: $OUT_BASELINE/"
echo ""
echo "Breakdown analysis:"
echo "  python scripts/analyze_breakdown.py $OUT_ELASTIC/breakdown.json"
echo ""
echo "================================================================"
echo "  Done. $(date)"
echo "================================================================"
