#!/bin/bash
#
# DGX Spark デュアル構成セットアップスクリプト
# Usage: curl -sL <url> | bash -s -- [command]
#
# Commands:
#   network    - QSFPインターフェースのIP設定
#   ssh        - 対向ノードへのSSH鍵配布
#   docker     - Docker権限設定
#   vllm-pull  - vLLMイメージ取得
#   cluster    - vLLMクラスター起動
#   all        - 全セットアップ実行
#
set -euo pipefail

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

log_info()  { echo -e "${BLUE}[INFO]${NC} $1"; }
log_ok()    { echo -e "${GREEN}[OK]${NC} $1"; }
log_warn()  { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }

# QSFPインターフェース検出
detect_qsfp_interface() {
    local iface
    iface=$(ibdev2netdev 2>/dev/null | grep "(Up)" | awk '{print $5}' | head -1)
    if [[ -z "$iface" ]]; then
        # フォールバック: enp1s0f で始まるインターフェースを探す
        iface=$(ip link show | grep -oP 'enp1s0f[0-9]+np[0-9]+' | head -1)
    fi
    echo "$iface"
}

# 現在のIPアドレス取得
get_current_ip() {
    local iface=$1
    ip -4 addr show "$iface" 2>/dev/null | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | head -1
}

# ネットワーク設定
cmd_network() {
    log_info "QSFPインターフェースを検出中..."
    
    local iface
    iface=$(detect_qsfp_interface)
    
    if [[ -z "$iface" ]]; then
        log_error "QSFPインターフェースが見つかりません"
        log_info "ibdev2netdev の出力を確認してください"
        exit 1
    fi
    
    log_ok "検出: $iface"
    
    # 現在のIP確認
    local current_ip
    current_ip=$(get_current_ip "$iface")
    
    if [[ -n "$current_ip" ]]; then
        log_info "現在のIP: $current_ip"
        read -rp "このIPを使用しますか？ [Y/n]: " use_current
        if [[ "${use_current,,}" != "n" ]]; then
            log_ok "設定完了: $iface = $current_ip"
            return 0
        fi
    fi
    
    # IP入力
    echo ""
    log_info "このノードのIPアドレスを設定します"
    log_info "例: Node 1 = 192.168.100.10, Node 2 = 192.168.100.11"
    read -rp "IPアドレス (例: 192.168.100.10): " new_ip
    read -rp "サブネットマスク [24]: " subnet
    subnet=${subnet:-24}
    
    # 設定適用
    log_info "IPアドレスを設定中..."
    sudo ip addr flush dev "$iface" 2>/dev/null || true
    sudo ip addr add "${new_ip}/${subnet}" dev "$iface"
    sudo ip link set "$iface" up
    
    log_ok "設定完了: $iface = $new_ip/$subnet"
    
    # 永続化確認
    read -rp "netplanで永続化しますか？ [Y/n]: " persist
    if [[ "${persist,,}" != "n" ]]; then
        local netplan_file="/etc/netplan/99-dgx-spark-qsfp.yaml"
        log_info "netplan設定を作成中..."
        
        sudo tee "$netplan_file" > /dev/null << EOF
network:
  version: 2
  ethernets:
    ${iface}:
      addresses:
        - ${new_ip}/${subnet}
EOF
        
        sudo netplan apply
        log_ok "永続化完了: $netplan_file"
    fi
    
    # 環境変数出力
    echo ""
    log_info "以下の環境変数をエクスポートしてください:"
    echo "export MN_IF_NAME=$iface"
    echo "export VLLM_HOST_IP=$new_ip"
}

# SSH鍵配布
cmd_ssh() {
    log_info "SSH鍵の設定を開始します"
    
    # 鍵がなければ生成
    if [[ ! -f ~/.ssh/id_ed25519 ]]; then
        log_info "SSH鍵を生成中..."
        ssh-keygen -t ed25519 -N "" -f ~/.ssh/id_ed25519
        log_ok "鍵生成完了"
    else
        log_ok "既存の鍵を使用: ~/.ssh/id_ed25519"
    fi
    
    # 対向ノードのIP入力
    read -rp "対向ノードのIPアドレス: " remote_ip
    read -rp "対向ノードのユーザー名 [$USER]: " remote_user
    remote_user=${remote_user:-$USER}
    
    log_info "対向ノードに公開鍵を配布中..."
    log_warn "パスワードを求められます"
    
    ssh-copy-id "${remote_user}@${remote_ip}"
    
    log_ok "SSH鍵配布完了"
    
    # 疎通確認
    log_info "接続テスト中..."
    if ssh -o BatchMode=yes -o ConnectTimeout=5 "${remote_user}@${remote_ip}" "echo OK" &>/dev/null; then
        log_ok "パスワードなしSSH接続成功"
    else
        log_error "接続に失敗しました"
        exit 1
    fi
}

# Docker権限設定
cmd_docker() {
    log_info "Docker権限を設定中..."
    
    if groups | grep -q docker; then
        log_ok "既にdockerグループに所属しています"
    else
        sudo groupadd docker 2>/dev/null || true
        sudo usermod -aG docker "$USER"
        log_ok "dockerグループに追加しました"
        log_warn "変更を反映するには再ログインするか 'newgrp docker' を実行してください"
    fi
    
    # NVIDIA Container Toolkit確認
    if docker run --rm --gpus all nvidia/cuda:13.0.1-devel-ubuntu24.04 nvidia-smi &>/dev/null; then
        log_ok "NVIDIA Container Toolkit正常動作"
    else
        log_error "GPU付きコンテナが起動できません"
        log_info "NVIDIA Container Toolkitをインストールしてください"
        exit 1
    fi
}

# vLLMイメージ取得
cmd_vllm_pull() {
    local image="nvcr.io/nvidia/vllm:25.11-py3"
    
    log_info "vLLMイメージを取得中..."
    log_info "Image: $image"
    
    docker pull "$image"
    
    log_ok "取得完了"
    echo ""
    echo "export VLLM_IMAGE=$image"
}

# vLLMクラスター起動
cmd_cluster() {
    log_info "vLLMクラスターを起動します"
    
    # 環境変数確認
    if [[ -z "${VLLM_IMAGE:-}" ]]; then
        export VLLM_IMAGE="nvcr.io/nvidia/vllm:25.11-py3"
        log_warn "VLLM_IMAGE未設定、デフォルト使用: $VLLM_IMAGE"
    fi
    
    if [[ -z "${VLLM_HOST_IP:-}" ]]; then
        local iface
        iface=$(detect_qsfp_interface)
        if [[ -n "$iface" ]]; then
            VLLM_HOST_IP=$(get_current_ip "$iface")
        fi
        
        if [[ -z "${VLLM_HOST_IP:-}" ]]; then
            read -rp "このノードのクラスター通信用IP: " VLLM_HOST_IP
        fi
    fi
    
    if [[ -z "${MN_IF_NAME:-}" ]]; then
        MN_IF_NAME=$(detect_qsfp_interface)
    fi
    
    log_info "設定:"
    echo "  VLLM_IMAGE:   $VLLM_IMAGE"
    echo "  VLLM_HOST_IP: $VLLM_HOST_IP"
    echo "  MN_IF_NAME:   $MN_IF_NAME"
    echo ""
    
    # ノードタイプ選択
    echo "このノードの役割を選択してください:"
    echo "  1) ヘッドノード (Node 1)"
    echo "  2) ワーカーノード (Node 2)"
    read -rp "選択 [1/2]: " node_type
    
    # run_cluster.sh取得
    if [[ ! -f ./run_cluster.sh ]]; then
        log_info "run_cluster.sh をダウンロード中..."
        wget -q https://raw.githubusercontent.com/vllm-project/vllm/refs/heads/main/examples/online_serving/run_cluster.sh
        chmod +x run_cluster.sh
    fi
    
    local head_ip
    if [[ "$node_type" == "1" ]]; then
        head_ip="$VLLM_HOST_IP"
        log_info "ヘッドノードとして起動中..."
        
        bash run_cluster.sh "$VLLM_IMAGE" "$head_ip" --head ~/.cache/huggingface \
            -e VLLM_HOST_IP="$VLLM_HOST_IP" \
            -e UCX_NET_DEVICES="$MN_IF_NAME" \
            -e NCCL_SOCKET_IFNAME="$MN_IF_NAME" \
            -e OMPI_MCA_btl_tcp_if_include="$MN_IF_NAME" \
            -e GLOO_SOCKET_IFNAME="$MN_IF_NAME" \
            -e TP_SOCKET_IFNAME="$MN_IF_NAME" \
            -e RAY_memory_monitor_refresh_ms=0 \
            -e MASTER_ADDR="$head_ip"
    else
        read -rp "ヘッドノード(Node 1)のIP: " head_ip
        log_info "ワーカーノードとして起動中..."
        
        bash run_cluster.sh "$VLLM_IMAGE" "$head_ip" --worker ~/.cache/huggingface \
            -e VLLM_HOST_IP="$VLLM_HOST_IP" \
            -e UCX_NET_DEVICES="$MN_IF_NAME" \
            -e NCCL_SOCKET_IFNAME="$MN_IF_NAME" \
            -e OMPI_MCA_btl_tcp_if_include="$MN_IF_NAME" \
            -e GLOO_SOCKET_IFNAME="$MN_IF_NAME" \
            -e TP_SOCKET_IFNAME="$MN_IF_NAME" \
            -e RAY_memory_monitor_refresh_ms=0 \
            -e MASTER_ADDR="$head_ip"
    fi
}

# 全セットアップ
cmd_all() {
    log_info "=== DGX Spark デュアル構成 フルセットアップ ==="
    echo ""
    
    cmd_docker
    echo ""
    
    cmd_network
    echo ""
    
    read -rp "対向ノードへのSSH鍵配布を行いますか？ [Y/n]: " do_ssh
    if [[ "${do_ssh,,}" != "n" ]]; then
        cmd_ssh
        echo ""
    fi
    
    cmd_vllm_pull
    echo ""
    
    log_ok "セットアップ完了！"
    echo ""
    log_info "次のステップ:"
    echo "  1. 対向ノードでも同じスクリプトを実行"
    echo "  2. 両ノードで 'dgx-spark-setup.sh cluster' を実行"
    echo "  3. ヘッドノードでモデルを起動"
}

# ヘルプ
cmd_help() {
    cat << 'EOF'
DGX Spark デュアル構成セットアップスクリプト

Usage:
  dgx-spark-setup.sh <command>

Commands:
  network    QSFPインターフェースのIP設定
  ssh        対向ノードへのSSH鍵配布
  docker     Docker権限設定
  vllm-pull  vLLMイメージ取得
  cluster    vLLMクラスター起動
  all        全セットアップ実行（推奨）

Examples:
  # フルセットアップ
  ./dgx-spark-setup.sh all

  # ネットワークのみ設定
  ./dgx-spark-setup.sh network

  # クラスター起動
  export VLLM_HOST_IP=192.168.100.10
  export MN_IF_NAME=enp1s0f1np1
  ./dgx-spark-setup.sh cluster

ワンライナー実行:
  curl -sL https://example.com/dgx-spark-setup.sh | bash -s -- all
EOF
}

# メイン
main() {
    local cmd="${1:-help}"
    
    case "$cmd" in
        network)   cmd_network ;;
        ssh)       cmd_ssh ;;
        docker)    cmd_docker ;;
        vllm-pull) cmd_vllm_pull ;;
        cluster)   cmd_cluster ;;
        all)       cmd_all ;;
        help|--help|-h) cmd_help ;;
        *)
            log_error "Unknown command: $cmd"
            cmd_help
            exit 1
            ;;
    esac
}

main "$@"
