#!/usr/bin/env bash
# ═══════════════════════════════════════════════════════════
# GPU 训练机一键 Bootstrap · setup.sh
# ═══════════════════════════════════════════════════════════
# 签发: 铸渊 · ICE-GL-ZY001 · 国作登字-2026-A-00037559
#
# 在 zy-gpu-train (119.45.160.137 · Ubuntu 22.04 · V100×4) 上跑。
# 安装系统依赖 / Python / 训练栈 / coscmd → 准备好即可启动训练。
#
# 用法:
#   sudo bash setup.sh
#
# 必需环境变量（由 training-bootstrap.yml 通过 SSH 写入到
#   /opt/guanghu/training/.env，或冰朔本地 export）:
#
#   ZY_COS_SECRET_ID         腾讯云 SecretId
#   ZY_COS_SECRET_KEY        腾讯云 SecretKey
#   ZY_COS_BUCKET            sy-finetune-corpus-1317346199
#   ZY_COS_REGION            ap-guangzhou
#   GH_REPO_OWNER            qinfendebingshuo
#   GH_REPO_NAME             guanghulab
#   GH_DISPATCH_TOKEN        ZY_DISPATCH_TOKEN（用于 progress-reporter 回报）
#
# 不会启动训练 — 只做环境准备。启动训练用 start-training.sh。
# ═══════════════════════════════════════════════════════════

set -euo pipefail

ROOT="${ZY_TRAIN_ROOT:-/opt/guanghu/training}"
DATA_DIR="${ZY_TRAIN_DATA:-/data/guanghu}"
ENV_FILE="$ROOT/.env"

REPORTER="$ROOT/progress-reporter.sh"
report() {
  if [[ -x "$REPORTER" ]] && [[ -f "$ENV_FILE" ]]; then
    # shellcheck disable=SC1090
    set -a; source "$ENV_FILE"; set +a
    "$REPORTER" "$@" || true
  fi
}

echo "═══════════════════════════════════════════"
echo "🛠️ 铸渊 · GPU 训练机 Bootstrap 开始"
echo "时间: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
echo "═══════════════════════════════════════════"

# ── 加载 .env ──
if [[ -f "$ENV_FILE" ]]; then
  # shellcheck disable=SC1090
  set -a; source "$ENV_FILE"; set +a
fi

# ── 校验关键环境变量 ──
REQUIRED=(ZY_COS_SECRET_ID ZY_COS_SECRET_KEY ZY_COS_BUCKET ZY_COS_REGION GH_REPO_OWNER GH_REPO_NAME GH_DISPATCH_TOKEN)
for v in "${REQUIRED[@]}"; do
  if [[ -z "${!v:-}" ]]; then
    echo "❌ 必需环境变量 $v 未设置（检查 $ENV_FILE）" >&2
    exit 2
  fi
done

mkdir -p "$ROOT" "$DATA_DIR/raw" "$DATA_DIR/processed" "$DATA_DIR/checkpoints" "$DATA_DIR/eval"

report "bootstrapping" "环境配置开始" "" "Bootstrap started on $(hostname)"

# ── 1. 系统依赖 ──
echo "═══ 1/7 · 系统依赖 ═══"
export DEBIAN_FRONTEND=noninteractive
apt-get update -y
apt-get install -y --no-install-recommends \
  python3 python3-pip python3-venv \
  git curl wget unzip jq tmux htop \
  build-essential ca-certificates

report "bootstrapping" "系统依赖完成" "" "apt 包安装完成"

# ── 2. Python venv + 训练栈 ──
echo "═══ 2/7 · Python 训练栈 ═══"
if [[ ! -d "$ROOT/venv" ]]; then
  python3 -m venv "$ROOT/venv"
fi
# shellcheck disable=SC1091
source "$ROOT/venv/bin/activate"
pip install --upgrade pip wheel setuptools

# torch 走 CUDA 12.1 wheel（最接近 12.8 的 stable）
pip install --index-url https://download.pytorch.org/whl/cu121 \
  torch==2.4.1 torchvision torchaudio || \
  pip install torch==2.4.1 torchvision torchaudio

pip install \
  "transformers>=4.48.0" \
  "accelerate>=0.34.0" \
  "datasets>=2.21.0" \
  "peft>=0.13.0" \
  "deepspeed>=0.15.1" \
  "sentencepiece" \
  "protobuf" \
  "tqdm" \
  "tensorboard" \
  "modelscope>=1.18.0" \
  "huggingface_hub>=0.24.0" \
  "coscmd"

deactivate
report "bootstrapping" "Python 训练栈完成" "" "torch + transformers + accelerate + deepspeed + modelscope 安装完成"

# ── 3. coscmd 配置 ──
echo "═══ 3/7 · coscmd 配置 ═══"
"$ROOT/venv/bin/coscmd" config \
  -a "$ZY_COS_SECRET_ID" \
  -s "$ZY_COS_SECRET_KEY" \
  -b "$ZY_COS_BUCKET" \
  -r "$ZY_COS_REGION"

# 注: coscmd 默认即走 cos.{region}.myqcloud.com,腾讯云内网会自动路由,无需改 endpoint
report "bootstrapping" "COS 客户端配置完成" "" "coscmd configured for $ZY_COS_BUCKET ($ZY_COS_REGION)"

# ── 4. 拉取语料 ──
echo "═══ 4/7 · 下载语料 ═══"
report "downloading-corpus" "拉取 raw/ 目录" "" "coscmd download raw/ → $DATA_DIR/raw"
"$ROOT/venv/bin/coscmd" download -rs "raw/" "$DATA_DIR/raw/"

# 统计
RAW_COUNT=$(find "$DATA_DIR/raw" -type f | wc -l)
echo "raw 文件数: $RAW_COUNT"
report "downloading-corpus" "语料下载完成" \
  "$(printf '{"step":0,"total_steps":0}')" \
  "raw=${RAW_COUNT} files downloaded to $DATA_DIR/raw"

# ── 5. 下载开源模型 (Qwen2.5-7B from ModelScope) ──
echo "═══ 5/7 · 下载 Qwen2.5-7B 模型 ═══"
report "bootstrapping" "下载 Qwen2.5-7B 模型" "" "ModelScope snapshot_download qwen/Qwen2.5-7B"
ZY_TRAIN_DATA="$DATA_DIR" "$ROOT/venv/bin/python" "$ROOT/download-model.py"
MODEL_BYTES=$(du -sb "$DATA_DIR/models/Qwen2.5-7B" 2>/dev/null | awk '{print $1}')
MODEL_GB=$(awk "BEGIN{printf \"%.2f\",${MODEL_BYTES:-0}/1024/1024/1024}")
report "bootstrapping" "模型下载完成" "" "Qwen2.5-7B 已就绪 ${MODEL_GB} GiB at $DATA_DIR/models/Qwen2.5-7B"

# ── 6. 预处理语料 → SFT JSONL ──
echo "═══ 6/7 · 预处理语料 ═══"
report "preprocessing" "ChatGPT export + Notion zip → SFT JSONL" "" "running preprocess-corpus.py"
ZY_TRAIN_DATA="$DATA_DIR" "$ROOT/venv/bin/python" "$ROOT/preprocess-corpus.py"
SFT_LINES=$(wc -l < "$DATA_DIR/processed/sft.jsonl" 2>/dev/null || echo 0)
report "preprocessing" "预处理完成" \
  "$(printf '{"step":0,"total_steps":0}')" \
  "SFT 样本数=${SFT_LINES} 写入 $DATA_DIR/processed/sft.jsonl"

# ── 7. 训练辅助脚本 ──
echo "═══ 7/7 · 安装训练辅助脚本 ═══"
chmod +x "$ROOT/progress-reporter.sh" "$ROOT/start-training.sh" "$ROOT/watch-training-output.sh" 2>/dev/null || true

cat > /etc/cron.d/zy-training-heartbeat <<'CRON'
# 铸渊副将 · 训练心跳兜底（每 5 分钟即使训练脚本崩了也能上报 GPU 状态）
*/5 * * * * root [ -f /opt/guanghu/training/.env ] && /opt/guanghu/training/progress-reporter.sh "$(cat /opt/guanghu/training/.phase 2>/dev/null || echo idle)" "" "" "heartbeat" >/dev/null 2>&1
CRON

echo "═══════════════════════════════════════════"
echo "✅ Bootstrap 完成"
echo "  训练根目录: $ROOT"
echo "  数据目录: $DATA_DIR"
echo "  下一步: bash $ROOT/start-training.sh"
echo "═══════════════════════════════════════════"
report "preprocessing" "Bootstrap 完成 · 等待启动训练" \
  "$(printf '{"step":0,"total_steps":0}')" \
  "Bootstrap done on $(hostname). raw=${RAW_COUNT} files. Ready to start training."

# 记录当前阶段（给 cron heartbeat 用）
echo "preprocessing" > "$ROOT/.phase"
