#!/bin/bash
# Run the complete experiment matrix:
#   1. Combined TP=2 DP=4 (4 instances, baseline)
#   2. Combined TP=1 DP=8 (8 instances, max throughput)
#   3. PD-Sep TP=1: P×4 + D×4 via Mooncake/RDMA
#
# All use the same trace, same concurrency, same timeout.

set -euo pipefail

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

MODEL="${MODEL_PATH:-$HOME/models/Qwen/Qwen3-Coder-30B-A3B-Instruct}"
TRACE="$PROJECT_DIR/traces/sampled_1000req_seed42.jsonl"

# Uniform benchmark params
MAX_SESSIONS=${MAX_SESSIONS:-8}
MAX_CONCURRENT=${MAX_CONCURRENT:-16}
TIME_SCALE=10
REQUEST_TIMEOUT=${REQUEST_TIMEOUT:-300}
REQUEST_LIMIT="${REQUEST_LIMIT:-}"  # empty = all 1000

cleanup_gpu() {
    pkill -9 -f "vllm" 2>/dev/null || true
    pkill -9 -f "cache_aware_proxy\|mooncake_connector_proxy\|uvicorn" 2>/dev/null || true
    fuser 9090/tcp 8000/tcp 2>/dev/null | xargs -r kill -9 2>/dev/null || true
    sleep 5
    fuser /dev/nvidia* 2>/dev/null | tr " " "\n" | sort -u | xargs -r kill -9 2>/dev/null || true
    sleep 10
}

wait_for_server() {
    local port=$1
    local timeout=${2:-600}
    timeout "$timeout" bash -c "until curl -s localhost:$port/v1/models >/dev/null 2>&1; do sleep 5; done"
}

run_benchmark() {
    local tag=$1
    local endpoint=$2
    local extra_args="${3:-}"
    local outdir="$PROJECT_DIR/outputs/$tag"

    echo "  Running benchmark -> $outdir"
    local limit_arg=""
    if [ -n "$REQUEST_LIMIT" ]; then
        limit_arg="--request-limit $REQUEST_LIMIT"
    fi

    $PYTHON -m replayer \
        --trace "$TRACE" \
        --output "$outdir/metrics.jsonl" \
        --endpoint "$endpoint" \
        --model "$MODEL" \
        --time-scale $TIME_SCALE \
        --max-inflight-sessions $MAX_SESSIONS \
        --concurrency-limit $MAX_CONCURRENT \
        --request-timeout $REQUEST_TIMEOUT \
        $limit_arg \
        -v

    echo "  Done: $(wc -l < "$outdir/metrics.jsonl") requests"
}

#######################################################################
# Experiment 1: Combined TP=2 DP=4
#######################################################################
run_combined_tp2_dp4() {
    echo ""
    echo "================================================================"
    echo "  Experiment 1: Combined TP=2 DP=4 (4 instances on 8 GPUs)"
    echo "================================================================"
    cleanup_gpu

    for i in 0 1 2 3; do
        local gpu_start=$((i * 2))
        local gpu_end=$((gpu_start + 1))
        local port=$((8000 + i))
        echo "  Starting instance $i: GPUs $gpu_start,$gpu_end, port $port"
        CUDA_VISIBLE_DEVICES=$gpu_start,$gpu_end $VLLM serve "$MODEL" \
            --host 0.0.0.0 --port $port \
            --tensor-parallel-size 2 \
            --trust-remote-code --enable-prefix-caching --enforce-eager \
            --dtype auto --gpu-memory-utilization 0.9 --max-model-len 200000 &
    done

    for i in 0 1 2 3; do
        wait_for_server $((8000 + i))
        echo "    Instance $i ready"
    done
    echo "  All 4 instances ready"

    # Start global scheduler (cache-aware proxy in combined mode)
    echo "  Starting global scheduler..."
    $PYTHON "$PROJECT_DIR/scripts/cache_aware_proxy.py" \
        --combined http://127.0.0.1:8000 http://127.0.0.1:8001 http://127.0.0.1:8002 http://127.0.0.1:8003 \
        --port 9090 &
    sleep 5

    run_benchmark "exp1_combined_tp2_dp4" "http://localhost:9090"
}

#######################################################################
# Experiment 2: Combined TP=1 DP=8
#######################################################################
run_combined_tp1_dp8() {
    echo ""
    echo "================================================================"
    echo "  Experiment 2: Combined TP=1 DP=8 (8 instances on 8 GPUs)"
    echo "================================================================"
    cleanup_gpu

    for i in $(seq 0 7); do
        local port=$((8000 + i))
        echo "  Starting instance $i: GPU $i, port $port"
        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 &
    done

    for i in $(seq 0 7); do
        wait_for_server $((8000 + i))
        echo "    Instance $i ready"
    done
    echo "  All 8 instances ready"

    # Start global scheduler (cache-aware proxy in combined mode)
    echo "  Starting global scheduler..."
    $PYTHON "$PROJECT_DIR/scripts/cache_aware_proxy.py" \
        --combined http://127.0.0.1:8000 http://127.0.0.1:8001 http://127.0.0.1:8002 http://127.0.0.1:8003 \
                   http://127.0.0.1:8004 http://127.0.0.1:8005 http://127.0.0.1:8006 http://127.0.0.1:8007 \
        --port 9090 &
    sleep 5

    run_benchmark "exp2_combined_tp1_dp8" "http://localhost:9090"
}

#######################################################################
# Experiment 3: PD-Sep TP=1 P×4 D×4 (Mooncake/RDMA)
#######################################################################
run_pd_sep_tp1() {
    echo ""
    echo "================================================================"
    echo "  Experiment 3: PD-Sep TP=1 P×4 + D×4 (Mooncake/RDMA)"
    echo "================================================================"
    cleanup_gpu

    PROXY_SCRIPT="$PROJECT_DIR/scripts/cache_aware_proxy.py"

    # Start 4 prefill instances (GPUs 0-3)
    local prefill_args=""
    for i in 0 1 2 3; do
        local port=$((8010 + i))
        local bootstrap=$((8998 + i))
        echo "  Prefill $i: GPU $i, port $port, bootstrap $bootstrap"
        VLLM_MOONCAKE_BOOTSTRAP_PORT=$bootstrap \
        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_producer\"}" &
        prefill_args="$prefill_args --prefill http://127.0.0.1:$port $bootstrap"
    done

    # Start 4 decode instances (GPUs 4-7)
    local decode_args=""
    for i in 0 1 2 3; do
        local gpu=$((4 + i))
        local port=$((8020 + i))
        echo "  Decode $i: GPU $gpu, port $port"
        CUDA_VISIBLE_DEVICES=$gpu $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_consumer\",\"kv_load_failure_policy\":\"recompute\"}" &
        decode_args="$decode_args --decode http://127.0.0.1:$port"
    done

    # Wait for all instances
    for i in 0 1 2 3; do
        wait_for_server $((8010 + i))
        echo "    Prefill $i ready"
    done
    for i in 0 1 2 3; do
        wait_for_server $((8020 + i))
        echo "    Decode $i ready"
    done

    # Start proxy (wait for bootstrap to be queryable first)
    echo "  Waiting for bootstrap servers..."
    for bp in 8998 8999 9000 9001; do
        timeout 120 bash -c "until curl -s localhost:$bp/query > /dev/null 2>&1; do sleep 2; done"
        echo "    Bootstrap $bp ready"
    done

    echo "  Starting proxy on port 9000..."
    $PYTHON "$PROXY_SCRIPT" $prefill_args $decode_args --host 0.0.0.0 --port 9090 &
    sleep 15

    # Smoke test with retry
    echo "  Smoke test..."
    for attempt in 1 2 3; do
        result=$(curl -s -m 120 http://localhost:9090/v1/completions \
            -X POST -H "Content-Type: application/json" \
            -d "{\"model\":\"$MODEL\",\"prompt\":[100,200,300],\"max_tokens\":3,\"temperature\":0}" 2>&1)
        if echo "$result" | grep -q "choices"; then
            echo "  Smoke test passed!"
            break
        fi
        echo "  Attempt $attempt failed, retrying..."
        sleep 10
    done

    run_benchmark "exp3_pd_sep_tp1_mooncake" "http://localhost:9090"
}

#######################################################################
# Main
#######################################################################
echo "Starting experiment matrix on $(hostname)"
echo "Model: $MODEL"
echo "Trace: $TRACE"
echo "Params: sessions=$MAX_SESSIONS, concurrent=$MAX_CONCURRENT, time_scale=$TIME_SCALE"
echo ""

case "${1:-all}" in
    1|tp2dp4)  run_combined_tp2_dp4 ;;
    2|tp1dp8)  run_combined_tp1_dp8 ;;
    3|pdsep)   run_pd_sep_tp1 ;;
    all)
        run_combined_tp2_dp4
        run_combined_tp1_dp8
        run_pd_sep_tp1
        ;;
    *)
        echo "Usage: $0 {1|2|3|all|tp2dp4|tp1dp8|pdsep}"
        exit 1
        ;;
esac

echo ""
echo "================================================================"
echo "  All experiments complete!"
echo "================================================================"
cleanup_gpu
