#!/usr/bin/env bash
# boss-byom.sh — 用你自己的模型跑完 boss 判断流水线 (BYOM 三连), 一条命令。
#
# 依赖: bash 4+, curl, jq
# 用法:
#     export BOSS_SVC_TOKEN=...      # 向服务管理员申请
#     export ANTHROPIC_API_KEY=...
#     ./boss-byom.sh "评估 X 产品线明年是否扩张"        > report.md
#     ./boss-byom.sh --doc plan.md                      > report.md
#
# 环境变量:
#     BOSS_SVC_URL      服务地址 (默认 https://svc.apex.fan)
#     BOSS_SVC_TOKEN    服务 token (必填)
#     ANTHROPIC_API_KEY 模型 key (必填)
#     ANTHROPIC_MODEL   模型 (默认 claude-sonnet-5)
#
# 想换模型供应商, 只改 call_model() 一个函数。
# 需要并发跑评委 / 更细的错误处理, 用 Python 版: boss_byom.py
#
# 报告走 stdout, 进度走 stderr —— 所以 `> report.md` 只会拿到报告。

set -euo pipefail

BOSS="${BOSS_SVC_URL:-https://svc.apex.fan}"
MODEL="${ANTHROPIC_MODEL:-claude-sonnet-5}"
SCENE="${BOSS_SCENE:-default}"

log() { printf '%s\n' "$*" >&2; }
die() { log "✗ $*"; exit 1; }

# ── 参数 (先于环境检查 —— --help 不该要凭证) ──────────────────────────
TOPIC=""; DOC=""
while [ $# -gt 0 ]; do
  case "$1" in
    --doc)   DOC="$2"; shift 2 ;;
    --scene) SCENE="$2"; shift 2 ;;
    -h|--help) sed -n '2,20p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
    *)       TOPIC="$1"; shift ;;
  esac
done

command -v jq  >/dev/null || die "缺 jq (brew install jq / apt install jq)"
[ -n "${BOSS_SVC_TOKEN:-}" ]    || die "缺 BOSS_SVC_TOKEN"
[ -n "${ANTHROPIC_API_KEY:-}" ] || die "缺 ANTHROPIC_API_KEY"
[ -n "$TOPIC" ] || [ -n "$DOC" ] || die "议题与 --doc 至少给一个"

# ── 调 boss 服务 ──────────────────────────────────────────────────────
boss_post() {   # boss_post <path> <json>
  local out code
  out=$(curl -sS -w $'\n%{http_code}' "$BOSS$1" \
        -H "Authorization: Bearer $BOSS_SVC_TOKEN" \
        -H "Content-Type: application/json" -d "$2")
  code="${out##*$'\n'}"; out="${out%$'\n'*}"
  if [ "$code" != "200" ]; then
    log "✗ HTTP $code ← $1"; log "  ${out:0:600}"
    case "$code" in
      401) log "  提示: token 不对。确认用的是 BOSS_SVC_TOKEN (与 BOSS_API_TOKEN 不通用)。" ;;
      404) log "  提示: run 可能已过期 (TTL 24 小时), 或路径写错。" ;;
      422) log "  提示: review 校验未过 —— 全批原子。维度评委必填 adversarial_view 三字段。" ;;
      502) log "  提示: 多半是服务重启的几秒空窗, 等 10 秒重试。" ;;
      503) log "  提示: 服务端未配 token (fail-close) —— 不是你的问题, 找部署方。" ;;
    esac
    exit 1
  fi
  printf '%s' "$out"
}

# ── 调你自己的模型 (想换供应商就改这里) ───────────────────────────────
call_model() {  # call_model <system> <user> [max_tokens]
  jq -n --arg m "$MODEL" --arg s "$1" --arg u "$2" --argjson mt "${3:-4096}" \
     '{model:$m, max_tokens:$mt, system:$s, messages:[{role:"user",content:$u}]}' \
  | curl -sS https://api.anthropic.com/v1/messages \
      -H "x-api-key: $ANTHROPIC_API_KEY" \
      -H "anthropic-version: 2023-06-01" \
      -H "Content-Type: application/json" --data @- \
  | jq -r '(.content // [] | map(.text // "") | join("")) as $t
           | if $t == "" then ("模型调用失败: " + (.error.message // tostring) | halt_error(1)) else $t end'
}

# ── ① prepare ─────────────────────────────────────────────────────────
log "① prepare …"
if [ -n "$DOC" ]; then
  [ -f "$DOC" ] || die "文件不存在: $DOC"
  REQ=$(jq -n --arg s "$SCENE" --arg n "$(basename "$DOC")" --rawfile t "$DOC" \
        '{scene:$s, caller:"boss-byom.sh", review_doc:{name:$n, text:$t}}')
else
  REQ=$(jq -n --arg s "$SCENE" --arg t "$TOPIC" \
        '{scene:$s, caller:"boss-byom.sh", topic:$t}')
fi
PACK=$(boss_post /v1/prepare "$REQ")

RUN_ID=$(jq -r .run_id <<<"$PACK")
N=$(jq '.judges | length' <<<"$PACK")
log "   run_id=$RUN_ID · panel=$(jq -r .panel_name <<<"$PACK") · $N 位评委"

# ── ② synthesis (user_template 可直接用, 没有哨兵) ────────────────────
log "② synthesis …"
SYNTH=$(call_model "$(jq -r .synthesis_task.system <<<"$PACK")" \
                   "$(jq -r .synthesis_task.user_template <<<"$PACK")" 6144)

# ── ③ 逐评委打分 (互不可见 —— 独立打分是 panel 设计的核心) ───────────
log "③ $N 位评委打分 (串行; 要并发用 Python 版) …"
REVIEWS='[]'; MERGE_BLOCK=""
for i in $(seq 0 $((N - 1))); do
  SLUG=$(jq -r ".judges[$i].slug" <<<"$PACK")
  SYS=$(jq -r ".judges[$i].system" <<<"$PACK")
  # 哨兵必须用字符串替换, 不能用模板引擎 (正文可能含花括号)
  USR=$(jq -r --arg s "$SYNTH" '.judges['"$i"'].user_template | gsub("<<SYNTHESIS_MD>>"; $s)' <<<"$PACK")
  MD=$(call_model "$SYS" "$USR" 4096)
  REVIEWS=$(jq --arg j "$SLUG" --arg m "$MD" '. + [{judge:$j, review_md:$m}]' <<<"$REVIEWS")
  MERGE_BLOCK="${MERGE_BLOCK}## ${SLUG}"$'\n\n'"${MD}"$'\n\n---\n\n'
  log "   ✓ $SLUG ($(jq -r ".judges[$i].category" <<<"$PACK"))"
done

# ── ④ submit (全批原子) ───────────────────────────────────────────────
log "④ submit …"
SUB=$(boss_post /v1/submit "$(jq -n --arg r "$RUN_ID" --argjson rv "$REVIEWS" \
      '{run_id:$r, reviews:$rv}')")
log "   accepted: $(jq -r '.accepted | join(", ")' <<<"$SUB")"

# ── ⑤ merge ───────────────────────────────────────────────────────────
log "⑤ merge …"
MSYS=$(jq -r .merge_task.system <<<"$PACK")
MUSR=$(jq -r --arg s "$SYNTH" --arg r "$MERGE_BLOCK" \
       '.merge_task.user_template | gsub("<<SYNTHESIS_MD>>"; $s) | gsub("<<REVIEWS_MD>>"; $r)' <<<"$PACK")
PROSE=$(call_model "$MSYS" "$MUSR" 6144)

# ── ⑥ render ──────────────────────────────────────────────────────────
log "⑥ render …"
OUT=$(boss_post /v1/render "$(jq -n --arg r "$RUN_ID" --arg p "$PROSE" \
      '{run_id:$r, body_prose:$p}')")

log "   panel_summary: $(jq -c '.panel_summary // {}' <<<"$OUT" | head -c 200)"
jq -r .report_md <<<"$OUT"      # 报告走 stdout
