#!/usr/bin/env bash
# ==============================================================================
# LLM Runner: Unified Inference Server Orchestrator for llama-server
# Target Hardware: AMD ROCm / Radeon AI PRO R9700 (~28-30GB VRAM)
# ==============================================================================

set -euo pipefail

# Sanitize environment:
# TEMP/TMP are standard POSIX variables for temporary file directories.
# If set to a sampling temperature (float/number), ROCm/libstdc++ crashes during initialization!
if [ -n "${TEMP:-}" ] && [ ! -d "${TEMP}" ]; then
    TEMPERATURE="${TEMPERATURE:-${TEMP}}"
    unset TEMP
fi
if [ -n "${TMP:-}" ] && [ ! -d "${TMP}" ]; then
    unset TMP
fi

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
LOG_DIR="${LLM_LOG_DIR:-${LOG_DIR:-${SCRIPT_DIR}/logs}}"
mkdir -p "${LOG_DIR}"

# ------------------------------------------------------------------------------
# Helpers & Color Output
# ------------------------------------------------------------------------------
BOLD="\033[1m"
GREEN="\033[32m"
BLUE="\033[34m"
YELLOW="\033[33m"
RED="\033[31m"
CYAN="\033[36m"
RESET="\033[0m"

log_info()    { echo -e "${BLUE}[INFO]${RESET} $*"; }
log_success() { echo -e "${GREEN}[✓]${RESET} $*"; }
log_warn()    { echo -e "${YELLOW}[!]${RESET} $*"; }
log_error()   { echo -e "${RED}[✗]${RESET} $*" >&2; }

# Find llama-server binary
find_llama_server() {
    if command -v llama-server >/dev/null 2>&1; then
        command -v llama-server
    elif [ -x "/run/current-system/sw/bin/llama-server" ]; then
        echo "/run/current-system/sw/bin/llama-server"
    else
        log_error "llama-server executable not found in PATH or system profile!"
        exit 1
    fi
}
LLAMA_SERVER="$(find_llama_server)"

# ------------------------------------------------------------------------------
# Status & Inspection
# ------------------------------------------------------------------------------
check_health() {
    local port="${1:-11434}"
    local host="${2:-127.0.0.1}"
    if curl -s "http://${host}:${port}/health" 2>/dev/null | grep -q '"status":\s*"ok"\|"ok"'; then
        return 0
    fi
    return 1
}

cmd_status() {
    echo -e "${BOLD}=== Local LLM Server Status ===${RESET}"
    local ports=(11434 11435 11436)
    local any_running=0

    for port in "${ports[@]}"; do
        local pid
        pid=$(lsof -ti :"$port" 2>/dev/null || fuser "$port/tcp" 2>/dev/null || true)
        if [ -n "$pid" ]; then
            any_running=1
            local model_info
            model_info=$(curl -s "http://127.0.0.1:${port}/v1/models" 2>/dev/null | jq -r '.data[0].id // "unknown"' 2>/dev/null || echo "initializing...")
            echo -e "  Port ${CYAN}${port}${RESET}: ${GREEN}RUNNING${RESET} (PID: ${pid}) - Model: ${BOLD}${model_info}${RESET}"
        else
            echo -e "  Port ${CYAN}${port}${RESET}: ${YELLOW}STOPPED${RESET}"
        fi
    done

    echo ""
    return 0
}

# ------------------------------------------------------------------------------
# Stop Servers
# ------------------------------------------------------------------------------
cmd_stop() {
    local target="${1:-all}"
    log_info "Stopping server(s): ${target}..."

    if [ "$target" = "all" ]; then
        pkill -f "llama-server" 2>/dev/null || true
        log_success "All llama-server processes terminated."
    else
        fuser -k -9 "$target/tcp" 2>/dev/null || true
        log_success "Killed server on port ${target}."
    fi
}

# ------------------------------------------------------------------------------
# Launch Single Server Instance
# ------------------------------------------------------------------------------
launch_instance() {
    local name="$1"
    local model="$2"
    local port="$3"
    local ctx="$4"
    local mmproj="${5:-}"
    local template="${6:-}"
    local temp="${7:-0.6}"
    local top_p="${8:-0.95}"
    local top_k="${9:-40}"
    local min_p="${10:-0.05}"
    local ngl="${11:-99}"
    local load_mode="${12:-none}"
    local flash_attn="${13:-on}"
    local ctk="${14:-q8_0}"
    local ctv="${15:-q8_0}"
    local reasoning="${16:-false}"
    local log_file="${LOG_DIR}/${name}.log"

    if [ ! -f "$model" ]; then
        log_error "Model file not found: ${model}"
        exit 1
    fi

    # Ensure target port is free
    if fuser "$port/tcp" >/dev/null 2>&1; then
        log_warn "Port ${port} is in use. Stopping existing process on port ${port}..."
        fuser -k -9 "$port/tcp" 2>/dev/null || true
        sleep 1
    fi

    local flags=(
        -m "${model}"
        --host "${LLM_HOST:-${HOST:-0.0.0.0}}"
        --port "${port}"
        --ctx-size "${ctx}"
        -np "${LLM_PARALLEL:-${PARALLEL:-1}}"
        --n-gpu-layers "${ngl}"
        --load-mode "${load_mode}"
        --temp "${temp}"
        --top-p "${top_p}"
        --top-k "${top_k}"
        --min-p "${min_p}"
        -ctk "${ctk}"
        -ctv "${ctv}"
        -fa "${flash_attn}"
        -b "${LLM_BATCH_SIZE:-${BATCH_SIZE:-2048}}"
        -ub "${LLM_UBATCH_SIZE:-${UBATCH_SIZE:-512}}"
    )

    if [ -n "${mmproj}" ] && [ -f "${mmproj}" ]; then
        flags+=(--mmproj "${mmproj}" --image-min-tokens "${LLM_IMAGE_MIN_TOKENS:-${IMAGE_MIN_TOKENS:-1024}}")
    fi

    if [ -n "${template}" ] && [ -f "${template}" ]; then
        flags+=(--chat-template-file "${template}" --jinja)
    fi

    if [ "${reasoning}" = "true" ]; then
        flags+=(--reasoning-preserve)
    fi

    log_info "Starting ${BOLD}${name}${RESET} on port ${CYAN}${port}${RESET} (CTX: ${ctx})..."
    "${LLAMA_SERVER}" "${flags[@]}" > "${log_file}" 2>&1 &
    local s_pid=$!

    # Wait for initialization
    log_info "Waiting for ${name} to initialize (PID: ${s_pid})..."
    while true; do
        if ! kill -0 "${s_pid}" 2>/dev/null; then
            log_error "${name} failed to start! Last 20 log lines from ${log_file}:"
            tail -n 20 "${log_file}" >&2
            exit 1
        fi
        if check_health "${port}"; then
            break
        fi
        sleep 1
    done

    log_success "${name} is READY at http://localhost:${port} (PID: ${s_pid})"
    echo "${s_pid}"
}

# ------------------------------------------------------------------------------
# Presets
# ------------------------------------------------------------------------------
cmd_cluster() {
    log_info "Starting Multi-Agent Cluster (SuperQwen 27B + 2x Qwen 4B)..."
    pkill -f "llama-server" 2>/dev/null || true
    sleep 1

    # Trap to gracefully stop all background instances on Ctrl+C
    cleanup_cluster() {
        log_warn "Stopping cluster instances..."
        pkill -P $$ 2>/dev/null || true
        pkill -f "llama-server" 2>/dev/null || true
        exit 0
    }
    trap cleanup_cluster SIGINT SIGTERM EXIT

    # SuperQwen 3.8 27B (Planner)
    local planner_model="${LLM_PLANNER_MODEL:-${PLANNER_MODEL:-${LLM_MODEL:-${MODEL:-${SCRIPT_DIR}/models/Qwen3.8/Qwen3.8-27B-UD-Q4_K_XL.gguf}}}}"
    if [ ! -f "${planner_model}" ] && [ -f "${SCRIPT_DIR}/models/Qwen3.8/SuperQwen3.8-27b-abliterated-Q4_K_M.gguf" ]; then
        planner_model="${SCRIPT_DIR}/models/Qwen3.8/SuperQwen3.8-27b-abliterated-Q4_K_M.gguf"
    fi
    local planner_mmproj="${LLM_PLANNER_MMPROJ:-${PLANNER_MMPROJ:-${SCRIPT_DIR}/models/Qwen3.8/mmproj-SuperQwen3.8-27b-abliterated-Q8_0.gguf}}"
    if [ "${planner_mmproj}" = "none" ]; then planner_mmproj=""; fi
    local planner_tmpl="${LLM_PLANNER_TEMPLATE:-${PLANNER_TEMPLATE:-${SCRIPT_DIR}/templates/Qwen3.8/chat_template.jinja}}"

    # Qwen 3.5 4B (Workers)
    local worker_model="${LLM_WORKER_MODEL:-${WORKER_MODEL:-${SCRIPT_DIR}/models/Qwen3.5/Qwen3.5-4B-Q5_K_M.gguf}}"
    local worker_mmproj="${LLM_WORKER_MMPROJ:-${WORKER_MMPROJ:-${SCRIPT_DIR}/models/Qwen3.5/mmproj-BF16.gguf}}"
    if [ "${worker_mmproj}" = "none" ]; then worker_mmproj=""; fi
    local worker_tmpl="${LLM_WORKER_TEMPLATE:-${WORKER_TEMPLATE:-${SCRIPT_DIR}/templates/Qwen3.5/chat_template.jinja}}"

    # 1. Planner on 11434
    launch_instance "planner_27b" "$planner_model" "${LLM_PLANNER_PORT:-${PLANNER_PORT:-11434}}" "${LLM_PLANNER_CTX:-${PLANNER_CTX:-65536}}" \
        "$planner_mmproj" "$planner_tmpl" "${LLM_PLANNER_TEMP:-${PLANNER_TEMP:-0.6}}" "${LLM_PLANNER_TOP_P:-${PLANNER_TOP_P:-0.95}}" "${LLM_PLANNER_TOP_K:-${PLANNER_TOP_K:-40}}" \
        "${LLM_PLANNER_MIN_P:-${PLANNER_MIN_P:-0.05}}" "${LLM_PLANNER_NGL:-${PLANNER_NGL:-99}}" "${LLM_PLANNER_LOAD_MODE:-${PLANNER_LOAD_MODE:-none}}" "${LLM_FLASH_ATTN:-${FLASH_ATTN:-on}}" \
        "${LLM_PLANNER_CTK:-${PLANNER_CTK:-q8_0}}" "${LLM_PLANNER_CTV:-${PLANNER_CTV:-q8_0}}" "false"

    # 2. Worker 1 on 11435
    launch_instance "worker_4b_1" "$worker_model" "${LLM_WORKER1_PORT:-${WORKER1_PORT:-11435}}" "${LLM_WORKER1_CTX:-${WORKER1_CTX:-16384}}" \
        "$worker_mmproj" "$worker_tmpl" "${LLM_WORKER1_TEMP:-${WORKER1_TEMP:-0.7}}" "${LLM_WORKER1_TOP_P:-${WORKER1_TOP_P:-0.8}}" "${LLM_WORKER1_TOP_K:-${WORKER1_TOP_K:-20}}" \
        "${LLM_WORKER1_MIN_P:-${WORKER1_MIN_P:-0.05}}" "${LLM_WORKER1_NGL:-${WORKER1_NGL:-99}}" "${LLM_LOAD_MODE:-${LOAD_MODE:-mmap}}" "${LLM_FLASH_ATTN:-${FLASH_ATTN:-on}}" \
        "${LLM_WORKER1_CTK:-${WORKER1_CTK:-q8_0}}" "${LLM_WORKER1_CTV:-${WORKER1_CTV:-q8_0}}" "false"

    # 3. Worker 2 on 11436
    launch_instance "worker_4b_2" "$worker_model" "${LLM_WORKER2_PORT:-${WORKER2_PORT:-11436}}" "${LLM_WORKER2_CTX:-${WORKER2_CTX:-16384}}" \
        "$worker_mmproj" "$worker_tmpl" "${LLM_WORKER2_TEMP:-${WORKER2_TEMP:-0.7}}" "${LLM_WORKER2_TOP_P:-${WORKER2_TOP_P:-0.8}}" "${LLM_WORKER2_TOP_K:-${WORKER2_TOP_K:-20}}" \
        "${LLM_WORKER2_MIN_P:-${WORKER2_MIN_P:-0.05}}" "${LLM_WORKER2_NGL:-${WORKER2_NGL:-99}}" "${LLM_LOAD_MODE:-${LOAD_MODE:-mmap}}" "${LLM_FLASH_ATTN:-${FLASH_ATTN:-on}}" \
        "${LLM_WORKER2_CTK:-${WORKER2_CTK:-q8_0}}" "${LLM_WORKER2_CTV:-${WORKER2_CTV:-q8_0}}" "false"

    echo ""
    log_success "All 3 cluster servers are operational:"
    echo "  [Planner]     SuperQwen 3.8 27B -> http://localhost:${LLM_PLANNER_PORT:-${PLANNER_PORT:-11434}}"
    echo "  [Implementer] Qwen 3.5 4B #1    -> http://localhost:${LLM_WORKER1_PORT:-${WORKER1_PORT:-11435}}"
    echo "  [Implementer] Qwen 3.5 4B #2    -> http://localhost:${LLM_WORKER2_PORT:-${WORKER2_PORT:-11436}}"
    echo ""
    echo "Logs directory: ${LOG_DIR}/"
    echo "Press Ctrl+C to stop all instances gracefully."
    wait
}

cmd_muse() {
    log_info "Starting Muse-Glimmer 30B Solo Server (Max Context: 128K)..."
    pkill -f "llama-server" 2>/dev/null || true
    sleep 1
    local model="${LLM_MODEL:-${MODEL:-${SCRIPT_DIR}/models/muse-glimmer/Muse-Glimmer-30B-UD-Q4_K_XL.gguf}}"
    local mmproj="${LLM_MMPROJ:-${MMPROJ:-${SCRIPT_DIR}/models/muse-glimmer/mmproj-Muse-Glimmer-30B-Q8_0.gguf}}"
    if [ "${mmproj}" = "none" ]; then mmproj=""; fi
    local tmpl="${LLM_TEMPLATE:-${TEMPLATE:-${SCRIPT_DIR}/templates/muse-glimmer/chat_template.jinja}}"

    launch_instance "muse_solo" "$model" "${LLM_PORT:-${PORT:-11434}}" "${LLM_CTX:-${CTX:-131072}}" \
        "$mmproj" "$tmpl" "${LLM_TEMPERATURE:-${TEMPERATURE:-${SAMPLING_TEMP:-0.6}}}" \
        "${LLM_TOP_P:-${TOP_P:-0.95}}" "${LLM_TOP_K:-${TOP_K:-40}}" \
        "${LLM_MIN_P:-${MIN_P:-0.05}}" "${LLM_NGL:-${NGL:-99}}" \
        "${LLM_LOAD_MODE:-${LOAD_MODE:-none}}" "${LLM_FLASH_ATTN:-${FLASH_ATTN:-on}}" \
        "${LLM_CTK:-${CTK:-q8_0}}" "${LLM_CTV:-${CTV:-q8_0}}" \
        "${LLM_REASONING_PRESERVE:-${REASONING_PRESERVE:-true}}"

    echo ""
    echo "Streaming logs: tail -f ${LOG_DIR}/muse_solo.log"
    echo "Press Ctrl+C to stop."
    wait
}

cmd_superqwen() {
    log_info "Starting SuperQwen 3.8 27B Solo Server (Max Context: 256K)..."
    pkill -f "llama-server" 2>/dev/null || true
    sleep 1
    local model="${LLM_MODEL:-${MODEL:-${SCRIPT_DIR}/models/Qwen3.8/SuperQwen3.8-27b-abliterated-Q4_K_M.gguf}}"
    if [ ! -f "${model}" ] && [ -f "${SCRIPT_DIR}/models/Qwen3.8/Qwen3.8-27B-UD-Q4_K_XL.gguf" ]; then
        model="${SCRIPT_DIR}/models/Qwen3.8/Qwen3.8-27B-UD-Q4_K_XL.gguf"
    fi
    local mmproj="${LLM_MMPROJ:-${MMPROJ:-${SCRIPT_DIR}/models/Qwen3.8/mmproj-SuperQwen3.8-27b-abliterated-Q8_0.gguf}}"
    if [ "${mmproj}" = "none" ]; then mmproj=""; fi
    local tmpl="${LLM_TEMPLATE:-${TEMPLATE:-${SCRIPT_DIR}/templates/Qwen3.8/chat_template.jinja}}"

    launch_instance "superqwen_solo" "$model" "${LLM_PORT:-${PORT:-11434}}" "${LLM_CTX:-${CTX:-262144}}" \
        "$mmproj" "$tmpl" "${LLM_TEMPERATURE:-${TEMPERATURE:-${SAMPLING_TEMP:-0.6}}}" \
        "${LLM_TOP_P:-${TOP_P:-0.95}}" "${LLM_TOP_K:-${TOP_K:-40}}" \
        "${LLM_MIN_P:-${MIN_P:-0.05}}" "${LLM_NGL:-${NGL:-99}}" \
        "${LLM_LOAD_MODE:-${LOAD_MODE:-none}}" "${LLM_FLASH_ATTN:-${FLASH_ATTN:-on}}" \
        "${LLM_CTK:-${CTK:-q8_0}}" "${LLM_CTV:-${CTV:-q8_0}}" \
        "${LLM_REASONING_PRESERVE:-${REASONING_PRESERVE:-true}}"

    echo ""
    echo "Streaming logs: tail -f ${LOG_DIR}/superqwen_solo.log"
    echo "Press Ctrl+C to stop."
    wait
}

cmd_qwen4b() {
    log_info "Starting Qwen 3.5 4B Solo Server..."
    pkill -f "llama-server" 2>/dev/null || true
    sleep 1
    local model="${LLM_MODEL:-${MODEL:-${SCRIPT_DIR}/models/Qwen3.5/Qwen3.5-4B-Q5_K_M.gguf}}"
    local mmproj="${LLM_MMPROJ:-${MMPROJ:-${SCRIPT_DIR}/models/Qwen3.5/mmproj-BF16.gguf}}"
    if [ "${mmproj}" = "none" ]; then mmproj=""; fi
    local tmpl="${LLM_TEMPLATE:-${TEMPLATE:-${SCRIPT_DIR}/templates/Qwen3.5/chat_template.jinja}}"

    launch_instance "qwen4b_solo" "$model" "${LLM_PORT:-${PORT:-11434}}" "${LLM_CTX:-${CTX:-32768}}" \
        "$mmproj" "$tmpl" "${LLM_TEMPERATURE:-${TEMPERATURE:-${SAMPLING_TEMP:-0.7}}}" \
        "${LLM_TOP_P:-${TOP_P:-0.8}}" "${LLM_TOP_K:-${TOP_K:-20}}" \
        "${LLM_MIN_P:-${MIN_P:-0.05}}" "${LLM_NGL:-${NGL:-99}}" \
        "${LLM_LOAD_MODE:-${LOAD_MODE:-mmap}}" "${LLM_FLASH_ATTN:-${FLASH_ATTN:-on}}" \
        "${LLM_CTK:-${CTK:-q8_0}}" "${LLM_CTV:-${CTV:-q8_0}}" "false"

    echo ""
    echo "Streaming logs: tail -f ${LOG_DIR}/qwen4b_solo.log"
    echo "Press Ctrl+C to stop."
    wait
}

cmd_custom() {
    log_info "Starting Custom Server Instance..."
    pkill -f "llama-server" 2>/dev/null || true
    sleep 1
    local model="${LLM_MODEL:-${MODEL:-}}"
    if [ -z "${model}" ] || [ ! -f "${model}" ]; then
        log_error "No valid MODEL specified! Pass LLM_MODEL=/path/to/model.gguf"
        exit 1
    fi
    local mmproj="${LLM_MMPROJ:-${MMPROJ:-}}"
    if [ "${mmproj}" = "none" ]; then mmproj=""; fi
    local tmpl="${LLM_TEMPLATE:-${TEMPLATE:-}}"
    local name="custom_solo"

    launch_instance "${name}" "$model" "${LLM_PORT:-${PORT:-11434}}" "${LLM_CTX:-${CTX:-65536}}" \
        "$mmproj" "$tmpl" "${LLM_TEMPERATURE:-${TEMPERATURE:-${SAMPLING_TEMP:-0.6}}}" \
        "${LLM_TOP_P:-${TOP_P:-0.95}}" "${LLM_TOP_K:-${TOP_K:-40}}" \
        "${LLM_MIN_P:-${MIN_P:-0.05}}" "${LLM_NGL:-${NGL:-99}}" \
        "${LLM_LOAD_MODE:-${LOAD_MODE:-none}}" "${LLM_FLASH_ATTN:-${FLASH_ATTN:-on}}" \
        "${LLM_CTK:-${CTK:-q8_0}}" "${LLM_CTV:-${CTV:-q8_0}}" \
        "${LLM_REASONING_PRESERVE:-${REASONING_PRESERVE:-false}}"

    echo ""
    echo "Streaming logs: tail -f ${LOG_DIR}/${name}.log"
    echo "Press Ctrl+C to stop."
    wait
}

# ------------------------------------------------------------------------------
# Help / Usage
# ------------------------------------------------------------------------------
cmd_help() {
    cat <<EOF
Usage: $(basename "$0") <command> [options]

Commands:
  cluster       Launch multi-agent cluster (SuperQwen 27B on 11434, 2x Qwen 4B on 11435/11436)
  muse          Launch Muse-Glimmer 30B Solo (128K context, port 11434)
  superqwen     Launch SuperQwen 3.8 27B Solo (256K context, port 11434)
  qwen4b        Launch Qwen 3.5 4B Solo (port 11434)
  custom        Launch custom model instance (requires LLM_MODEL=/path/to/model.gguf)
  status        Show current server health and active ports
  stop [target] Stop servers (target: "all" or port number like 11434)
  help          Display this help message

Environment Variables:
  LLM_MODEL         Path to GGUF model file (fallback: MODEL)
  LLM_MMPROJ        Path to mmproj file, or "none" (fallback: MMPROJ)
  LLM_TEMPLATE      Path to chat template file (fallback: TEMPLATE)
  LLM_HOST          Bind address (default: 0.0.0.0)
  LLM_PORT          Main port override (default: 11434)
  LLM_CTX           Context size override (default: preset-specific)
  LLM_TEMPERATURE   Sampling temperature (default: preset-specific)
  LLM_PARALLEL      Parallel slot count (default: 1)
  LLM_FLASH_ATTN    Enable Flash Attention: "on" or "off" (default: on)
  LLM_LOG_DIR       Path to write logs (default: ./logs)

EOF
}

# ------------------------------------------------------------------------------
# Dispatch
# ------------------------------------------------------------------------------
ACTION="${1:-help}"
shift || true

case "${ACTION}" in
    cluster)    cmd_cluster "$@" ;;
    muse)       cmd_muse "$@" ;;
    superqwen)  cmd_superqwen "$@" ;;
    qwen4b)     cmd_qwen4b "$@" ;;
    custom)     cmd_custom "$@" ;;
    status)     cmd_status "$@" ;;
    stop)       cmd_stop "$@" ;;
    help|--help|-h) cmd_help ;;
    *)
        log_error "Unknown command: ${ACTION}"
        cmd_help
        exit 1
        ;;
esac
