#!/bin/sh
# 独行录 / opcmenu —— agent 一键接入
#
#   curl -fsSL https://opcmenu.com/install.sh | sh
#
# 干的事：手机号 + 短信验证码登录 → 自动为这台机器新建（或替换）一台「设备」并签发
# 长效可吊销密钥 → 写进你机器上每个检测到的 agent 的 MCP 配置（Codex / Claude Code /
# OpenClaw / Hermes / WorkBuddy）→ 把密钥存到 ~/.config/opcmenu/credentials.json。
# 全程不需要打开网页复制 token。
#
# 非交互（CI / 远程 / 复跑）：
#   sh install.sh --phone 1xxxxxxxxxx --code 123456 [--client codex] [--name "我的本"]
#   OPCMENU_TOKEN=<jwt> sh install.sh            # 已有密钥，只写配置
# 可覆盖地址：OPCMENU_API_BASE、OPCMENU_MCP_URL。
set -eu

MCP_URL="${OPCMENU_MCP_URL:-https://mcp.opcmenu.com/mcp}"
BASE_API="${OPCMENU_API_BASE:-https://api.opcmenu.com}"
SITE="${OPCMENU_SITE:-https://opcmenu.com}"

PHONE=""; CODE=""; CLIENT=""; NAME=""; TOKEN="${OPCMENU_TOKEN:-}"; REPLACE=""; ASSUME_YES=0
DEVICE_ID=""

while [ $# -gt 0 ]; do
  case "$1" in
    --phone) PHONE="$2"; shift 2;;
    --code) CODE="$2"; shift 2;;
    --client) CLIENT="$2"; shift 2;;
    --name) NAME="$2"; shift 2;;
    --token) TOKEN="$2"; shift 2;;
    --replace) REPLACE="$2"; shift 2;;
    --yes|-y) ASSUME_YES=1; shift;;
    -h|--help)
      echo "用法: sh install.sh [--phone 1xxxxxxxxxx --code 123456] [--client codex|claude-code|openclaw|hermes|workbuddy] [--name 设备名] [--token <jwt>] [--yes]"
      exit 0;;
    *) echo "未知参数: $1" >&2; exit 2;;
  esac
done

# ── 小工具 ────────────────────────────────────────────────────────────────
RED=''; GRN=''; YLW=''; DIM=''; BLD=''; RST=''
if [ -t 1 ]; then RED=$(printf '\033[31m'); GRN=$(printf '\033[32m'); YLW=$(printf '\033[33m'); DIM=$(printf '\033[2m'); BLD=$(printf '\033[1m'); RST=$(printf '\033[0m'); fi
info() { printf '%s\n' "$*"; }
ok()   { printf '  %s✓%s %s\n' "$GRN" "$RST" "$*"; }
warn() { printf '  %s!%s %s\n' "$YLW" "$RST" "$*"; }
skip() { printf '  %s–%s %s\n' "$DIM" "$RST" "$*"; }
die()  { printf '%sERROR:%s %s\n' "$RED" "$RST" "$*" >&2; exit 1; }
have() { command -v "$1" >/dev/null 2>&1; }
have_jq() { have jq; }

# 控制终端：curl|sh 时 stdin 是脚本，交互输入要走 /dev/tty。
# 注意 /dev/tty 可能「存在却 not configured」（无控制终端，如经 ssh 的 curl|sh）——
# 必须真去打开它读一下才算数，不能只用 [ -e ]。
TTY=""
if { : < /dev/tty; } 2>/dev/null; then TTY=/dev/tty; fi
ask() { # ask VAR "提示" -> 读一行到 VAR
  _p="$2"
  if [ -z "$TTY" ]; then die "需要交互输入「$_p」但没有终端。请用参数：--phone/--code（详见 --help）。"; fi
  printf '%s' "$_p" > "$TTY"
  IFS= read -r _ans < "$TTY" || _ans=""
  eval "$1=\$_ans"
}

# 从 JSON 取值：优先 jq（准），否则 sed 兜底（够用，token 是 JWT 无引号）。
json_get() { # json_get '<json>' '<jq过滤>' '<sed的key>'
  if have_jq; then printf '%s' "$1" | jq -r "$2 // empty" 2>/dev/null; else
    printf '%s' "$1" | sed -n "s/.*\"$3\":\"\\([^\"]*\\)\".*/\\1/p" | head -n1; fi
}

curl_json() { # curl_json METHOD URL JSONBODY
  curl -fsS -X "$1" "$2" -H 'content-type: application/json' -H 'x-opc-client: agent' -d "$3"
}

# ── 1. 拿到密钥（手机号登录，或已给 --token）────────────────────────────────
banner() {
  info ""
  info "${BLD}独行录 · agent 接入${RST}  ${DIM}opcmenu.com${RST}"
  info "${DIM}把这台机器接进独行录 MCP，让你的 agent 以你的身份读写资料。${RST}"
  info ""
}

login_flow() {
  [ -n "$PHONE" ] || ask PHONE "手机号: "
  echo "$PHONE" | grep -Eq '^1[3-9][0-9]{9}$' || die "手机号格式不对：$PHONE"

  info "${DIM}正在发送验证码到 $PHONE …${RST}"
  RS=$(curl_json POST "$BASE_API/v1/auth/request-sms" "{\"phone\":\"$PHONE\",\"purpose\":\"login\"}") \
    || die "发送验证码失败（网络或被限流）。"
  case "$RS" in *'"ok":false'*) die "发送验证码被拒：$RS";; esac

  [ -n "$CODE" ] || ask CODE "短信验证码（6 位）: "
  echo "$CODE" | grep -Eq '^[0-9]{6}$' || die "验证码格式不对。"

  [ -z "$CLIENT" ] && [ -n "$TTY" ] && {
    info "这是给哪个 agent 接入？(回车=自动检测本机已装的全部)"
    info "  ${DIM}1) Codex   2) Claude Code   3) OpenClaw   4) Hermes   5) WorkBuddy${RST}"
    ask _c "选择 [回车=全部]: "
    case "$_c" in 1) CLIENT=codex;; 2) CLIENT=claude-code;; 3) CLIENT=openclaw;; 4) CLIENT=hermes;; 5) CLIENT=workbuddy;; esac
  }
  [ -n "$NAME" ] || NAME="$(hostname 2>/dev/null || echo agent)"

  BODY="{\"phone\":\"$PHONE\",\"code\":\"$CODE\",\"client\":\"${CLIENT:-other}\",\"deviceName\":\"$NAME\""
  [ -n "$REPLACE" ] && BODY="$BODY,\"replaceDeviceId\":\"$REPLACE\""
  BODY="$BODY}"

  RESP=$(curl_json POST "$BASE_API/v1/auth/agent" "$BODY") || die "登录失败（验证码错误或过期？）。"
  case "$RESP" in *'"ok":false'*) die "登录被拒：$(json_get "$RESP" '.error' error)";; esac

  TOKEN=$(json_get "$RESP" '.data.token' token)
  DEVICE_ID=$(json_get "$RESP" '.data.device.id' id)
  [ -n "$TOKEN" ] || die "没拿到密钥，返回：$RESP"
  ok "登录成功，已为本机新建设备：${BLD}$NAME${RST}"

  # 列出其它设备，提供「替换/吊销」选择（需要 jq）
  if [ -z "$REPLACE" ] && [ -n "$TTY" ] && have_jq; then
    OTHERS=$(printf '%s' "$RESP" | jq -r --arg me "$DEVICE_ID" '.data.devices[] | select(.revokedAt==null and .id!=$me) | "\(.id)\t\(.name) [\(.client//"?")]"' 2>/dev/null || true)
    if [ -n "$OTHERS" ]; then
      info ""
      info "你名下还有这些已接入的设备："
      i=0; printf '%s\n' "$OTHERS" | while IFS="$(printf '\t')" read -r id label; do i=$((i+1)); printf '  %s) %s\n' "$i" "$label"; done
      ask _r "要吊销其中某台旧设备吗？输入编号，或回车跳过: "
      if echo "$_r" | grep -Eq '^[0-9]+$'; then
        RID=$(printf '%s\n' "$OTHERS" | sed -n "${_r}p" | cut -f1)
        if [ -n "$RID" ]; then
          curl -fsS -X POST "$BASE_API/v1/me/devices/revoke" -H 'content-type: application/json' \
            -H "authorization: Bearer $TOKEN" -d "{\"id\":\"$RID\"}" >/dev/null 2>&1 \
            && ok "已吊销该旧设备。" || warn "吊销失败，可去 $SITE/dashboard 手动处理。"
        fi
      fi
    fi
  fi
}

# ── 2. 写各 agent 的 MCP 配置 ───────────────────────────────────────────────
persist_env() { # 把 token 持久化到 shell rc（Codex 只认环境变量，且在启动时读一次）
  for RC in "$HOME/.zshrc" "$HOME/.bashrc"; do
    touch "$RC" 2>/dev/null || continue
    # 先删掉旧的 OPCMENU_TOKEN export 行（无匹配时 grep 退出码=1，用 || true 兜住），再 append。
    grep -v "^export OPCMENU_TOKEN=" "$RC" > "$RC.opctmp" 2>/dev/null || true
    mv "$RC.opctmp" "$RC"
    printf 'export OPCMENU_TOKEN="%s"\n' "$TOKEN" >> "$RC"
  done
}

configure_codex() {
  CFG="$HOME/.codex/config.toml"; mkdir -p "$HOME/.codex"; touch "$CFG"
  if grep -q '^\[mcp_servers\.opcmenu\]' "$CFG" 2>/dev/null; then
    awk '/^\[mcp_servers\.opcmenu\]/{skip=1;next} skip&&/^\[/{skip=0} !skip{print}' "$CFG" > "$CFG.tmp" && mv "$CFG.tmp" "$CFG"
  fi
  # 去掉尾部空行，避免多次运行堆积空行
  awk 'NF{p=NR} {a[NR]=$0} END{for(i=1;i<=p;i++)print a[i]}' "$CFG" > "$CFG.tmp" && mv "$CFG.tmp" "$CFG"
  { [ -s "$CFG" ] && printf '\n'; printf '[mcp_servers.opcmenu]\nurl = "%s"\nbearer_token_env_var = "OPCMENU_TOKEN"\n' "$MCP_URL"; } >> "$CFG"
  persist_env
  ok "Codex: 写好 $CFG + 把密钥写进 ~/.zshrc 环境变量"
  warn "Codex 在启动时读一次环境变量：请${BLD}重开一个终端${RST}再运行 codex（或 \`exec zsh\`）。"
}

configure_claude() {
  if have claude; then
    claude mcp remove opcmenu --scope user >/dev/null 2>&1 || true
    if claude mcp add --transport http opcmenu "$MCP_URL" --scope user --header "Authorization: Bearer $TOKEN" >/dev/null 2>&1; then
      ok "Claude Code: 已 claude mcp add（密钥已内置到请求头）"
    else
      warn "Claude Code: claude mcp add 失败，请照 $SITE/connect 手动接入。"
    fi
    # 顺手装一个本地技能：让 Claude 学会随时帮你重连 + 用独行录（说「帮我用独行录…」即可）
    SKDIR="$HOME/.claude/skills/opcmenu"; mkdir -p "$SKDIR"
    curl -fsSL "$SITE/agents/opcmenu.skill.md" -o "$SKDIR/SKILL.md" 2>/dev/null \
      && ok "Claude Code: 顺手装好 opcmenu 技能" || true
  else
    skip "Claude Code: 未检测到 claude CLI"
  fi
}

configure_openclaw() {
  CFG="$HOME/.openclaw/openclaw.json"; mkdir -p "$HOME/.openclaw"
  if have_jq; then
    [ -s "$CFG" ] || printf '{}' > "$CFG"
    jq --arg url "$MCP_URL" --arg auth "Bearer $TOKEN" \
      '.mcp.servers.opcmenu = {url:$url, transport:"streamable-http", headers:{Authorization:$auth}}' \
      "$CFG" > "$CFG.tmp" && mv "$CFG.tmp" "$CFG"
    ok "OpenClaw: 写好 $CFG"
  else
    warn "OpenClaw: 缺 jq，无法安全合并。手动加到 $CFG（见 $SITE/connect）。"
  fi
}

configure_hermes() {
  CFG="$HOME/.hermes/config.yaml"
  if have hermes && hermes mcp add opcmenu --url "$MCP_URL" --header "Authorization: Bearer $TOKEN" >/dev/null 2>&1; then
    ok "Hermes: 已 hermes mcp add"; return
  fi
  mkdir -p "$HOME/.hermes"
  if [ ! -s "$CFG" ]; then
    printf 'mcp_servers:\n  opcmenu:\n    url: "%s"\n    headers:\n      Authorization: "Bearer %s"\n' "$MCP_URL" "$TOKEN" > "$CFG"
    ok "Hermes: 写好 $CFG"
  else
    warn "Hermes: 已存在 config.yaml，未自动改写。手动加 mcp_servers.opcmenu（见 $SITE/connect）。"
  fi
}

configure_workbuddy() {
  DIR="$HOME/.workbuddy"; CFG="$DIR/mcp.json"; mkdir -p "$DIR"
  if have_jq; then
    [ -s "$CFG" ] || printf '{"mcpServers":{}}' > "$CFG"
    jq --arg url "$MCP_URL" --arg auth "Bearer $TOKEN" \
      '.mcpServers.opcmenu = {type:"http", url:$url, headers:{Authorization:$auth}}' \
      "$CFG" > "$CFG.tmp" && mv "$CFG.tmp" "$CFG"
    ok "WorkBuddy: 写好 $CFG（重启 WorkBuddy 生效）"
  else
    warn "WorkBuddy: 缺 jq。或在 App 内「插件 → MCP 服务器 → 配置 MCP」粘贴（见 $SITE/connect）。"
  fi
}

write_credentials() {
  DIR="$HOME/.config/opcmenu"; mkdir -p "$DIR"; chmod 700 "$DIR" 2>/dev/null || true
  cat > "$DIR/credentials.json" <<EOF
{
  "mcp_url": "$MCP_URL",
  "api_base_url": "$BASE_API",
  "access_token": "$TOKEN",
  "device_id": "$DEVICE_ID",
  "device_name": "$NAME",
  "client": "$CLIENT"
}
EOF
  chmod 600 "$DIR/credentials.json" 2>/dev/null || true
}

configure_one() {
  case "$1" in
    codex) configure_codex;;
    claude-code) configure_claude;;
    openclaw) configure_openclaw;;
    hermes) configure_hermes;;
    workbuddy) configure_workbuddy;;
    *) warn "未知客户端：$1";;
  esac
}

configure_all() {
  info ""
  info "${BLD}接入各 agent${RST}${DIM}（只配置检测到的）${RST}"
  # 指定了 --client 就只配那一个（即便未检测到也预写配置）。
  if [ -n "$CLIENT" ]; then configure_one "$CLIENT"; return; fi
  done_any=0
  if have codex    || [ -d "$HOME/.codex" ];    then configure_codex;     done_any=1; fi
  if have claude   || [ -d "$HOME/.claude" ];   then configure_claude;    done_any=1; fi
  if have openclaw || [ -d "$HOME/.openclaw" ]; then configure_openclaw;  done_any=1; fi
  if have hermes   || [ -d "$HOME/.hermes" ];   then configure_hermes;    done_any=1; fi
  if [ -d "$HOME/.workbuddy" ];                 then configure_workbuddy; done_any=1; fi
  if [ "$done_any" = 0 ]; then
    warn "没检测到 Codex / Claude Code / OpenClaw / Hermes / WorkBuddy。"
    warn "密钥已存到 ~/.config/opcmenu/credentials.json，装好 agent 后再跑一次即可。"
  fi
}

# ── 主流程 ─────────────────────────────────────────────────────────────────
banner
if [ -z "$TOKEN" ]; then login_flow; else info "${DIM}使用已提供的密钥（--token / OPCMENU_TOKEN）。${RST}"; fi
[ -n "$TOKEN" ] || die "没有可用密钥。"
write_credentials
configure_all

info ""
info "${GRN}${BLD}接好了。${RST}试着跟你的 agent 说："
info "  ${DIM}在独行录上搜做「AI 笔记」的产品，挑 5 个最相关的给我。${RST}"
info "  ${DIM}帮我在独行录完成入驻：先看我还差哪步，再把资料和产品填详细。${RST}"
info "管理已接入的设备（可随时吊销）：${DIM}$SITE/dashboard${RST}"
info ""
