#!/usr/bin/env bash
# ThinSpider 爬虫引擎 一键安装加载器（由 SN 许可服务渲染）
#
# 本脚本只做两件事：定位/下载"核心安装包"（内含 TUI 向导），然后透传全部
# 参数 exec 包内向导。环境检测、冲突处理、依赖安装全部由向导完成。
#
# 用法：
#   curl -fsSL 'https://sn.thinkalike.com.cn/install.sh?product=THINSPIDER' | bash -s -- --sn <SN码>
# 常用选项（完整选项由向导 --help 给出）：
#   --sn <SN>      许可序列号（缺省自动申请 90 天试用）
#   --host <name>  nginx 对外主机名/IP
#   --yes          非交互模式（全部按推荐项执行）
#   --scope <s>    full|standard|minimal
#   --tmp <dir>    指定下载/解压的临时目录（默认自动挑一块够大的盘）
#   --dry-run      仅环境检测
#
# 离线采集模式：把 fetch-all.sh 采集到的 thinspider-core.tar.gz 放在本
# 脚本同目录，将直接复用（不联网下载）。
set -euo pipefail

SN_BASE="https://sn.thinkalike.com.cn"
CORE_URL="$SN_BASE/download/thinspider-core.tar.gz"
SN=""
ARGS=()

while [ $# -gt  ]; do
  case "$" in
    --sn) SN="${2:-}"; shift 2 ;;
    --tmp) TMP_OVERRIDE="${2:-}"; shift 2 ;;
    --sn-base) SN_BASE="${2:-}"; CORE_URL="$SN_BASE/download/thinspider-core.tar.gz"; ARGS+=(--sn-base "$SN_BASE"); shift 2 ;;
    *) ARGS+=("$"); shift ;;
  esac
done

[ "$(id -u)" -eq  ] || { echo "run as root"; exit ; }

# ---- 定位核心包：同目录携带 > 采集目录/当前目录 > 已下载缓存 > 在线下载（断点续传） ----
PKG=""
SRC="${BASH_SOURCE[]:-}"
if [ -n "$SRC" ]; then SELF_DIR="$(dirname "$SRC")"; else SELF_DIR="."; fi
# 说明书与 README 给了两条都能走通的路径（在 <code>-files 里直接跑、或先解压 core 再
# 从 <code>-core/ 里调 ../<code>-fullinstall/install.sh），加载器必须都找得到包；
# 否则离线交付会静默退化成重新公网下载数 GB（客户现场往往没网）。
for d in "$SELF_DIR" "$PWD" "$SELF_DIR/.." "$PWD/.."; do
  if [ -f "$d/thinspider-core.tar.gz" ]; then
    PKG="$d/thinspider-core.tar.gz"
    echo "[loader] 使用随包携带的核心包（离线采集模式）：$PKG"
    break
  fi
done

CORE_NAME="thinspider-core.tar.gz"
# SN 是 FastAPI，HEAD 返 4，只能用 Range 探针从 Content-Range 里拿总长。
probe_size() {
  curl -sS --connect-timeout  --max-time 3 -r - -D - -o /dev/null "$" 2>/dev/null \
    | tr -d '\r' | sed -n 's/^[Cc]ontent-[Rr]ange: bytes [-9]*-[-9]*\/\([-9][-9]*\)$/\/p' | head -n
}
avail_kb() { df -P -k "$" 2>/dev/null | awk 'NR==2{print $4}'; }

# 落地目录不能写死 /tmp：/tmp 常常是 tmpfs 内存盘（实测一台 3.G 只剩 .G），
# .4GB 的核心包下到一半就 ENOSPC，客户只看到一句“下载失败”。
SIZE_RAW="$(probe_size "$CORE_URL")"
SIZE="${SIZE_RAW:-}"
[ "$SIZE" -ge  ] 2>/dev/null || SIZE=
if [ "$SIZE" -eq  ]; then
  SIZE=$((2 * 24 * 24 * 24))   # 探不到就按 2GB 保守估，不拿“无上限”当默认
  echo "[loader] NOTE: 未探到核心包大小（离线镜像/代理屏蔽了 Content-Range），按 2GB 预估空间"
fi
# 下载一份 + 解压一份（实测核心包压缩 .39GB / 解压后 .23GB ≈ .9 份）+ 26MB 余量
NEED_KB=$(( SIZE / 24 * 2 + 26244 ))
NEED_GB=$(( (NEED_KB + 487) / 4876 ))
WORK=""
for d in "${TMP_OVERRIDE:-}" /var/tmp /tmp "$SELF_DIR"; do
  [ -n "$d" ] || continue
  [ -d "$d" ] && [ -w "$d" ] || continue
  KB="$(avail_kb "$d")"
  [ -n "$KB" ] || continue
  if [ "$KB" -ge "$NEED_KB" ]; then WORK="$d"; break; fi
done
if [ -z "$WORK" ]; then
  echo "[loader] ERROR: 找不到可写且剩余空间≥${NEED_GB}GB 的临时目录（依次试过 --tmp / /var/tmp / /tmp / 当前目录）。"
  echo "         用 --tmp <目录> 指一块数据盘，或先在联网机跑 fetch-all.sh 采集后离线安装。"
  exit 
fi
echo "[loader] 临时目录: $WORK（核心包 $((SIZE / 4876))MB，需约 ${NEED_GB}GB）"

# 复用缓存必须确认文件已完整：半截包直接交给 tar 只会报一看不懂的“invalid gzip”。
if [ -z "$PKG" ]; then
  for d in "$WORK" /tmp; do
    [ -f "$d/$CORE_NAME" ] || continue
    HAVE="$(stat -c %s "$d/$CORE_NAME" 2>/dev/null || echo )"
    if [ -z "$SIZE_RAW" ] || [ "$HAVE" = "$SIZE" ]; then
      PKG="$d/$CORE_NAME"
      echo "[loader] 复用已下载的核心包：$PKG"
      break
    fi
    echo "[loader] $d/$CORE_NAME 大小 $HAVE 与服务端 $SIZE 不符（上次中断），续传补齐"
  done
fi
if [ -z "$PKG" ]; then
  echo "[loader] 下载核心包: $CORE_URL"
  curl -fL --retry 3 -C - "$CORE_URL" -o "$WORK/$CORE_NAME" \
    || { echo "[loader] ERROR: 核心包下载失败（重跑本命令可断点续传；离线环境请先用 fetch-all.sh 采集，或把 $CORE_NAME 放到本脚本同目录）"; exit ; }
  PKG="$WORK/$CORE_NAME"
fi

# ---- 解压并移交向导 ----
STAGE="$(mktemp -d "$WORK/thinspider-core.XXXXXX")"
trap 'rm -rf "$STAGE"' EXIT
tar -xzf "$PKG" -C "$STAGE"
sed -i 's/\r$//' "$STAGE/install.sh" 2>/dev/null || true
[ -f "$STAGE/install.sh" ] || { echo "[loader] ERROR: 核心包内缺少 install.sh 向导"; exit ; }

# 采集模式的加选包（浏览器/ollama）若在同目录，一并带入向导可见位置
if [ -n "$SRC" ]; then
  for f in "$(dirname "$SRC")"/thinspider-browser-*.tar.gz \
           "$(dirname "$SRC")"/thinspider-ollama-*.tar.gz; do
    [ -f "$f" ] && cp -n "$f" "$STAGE/" 2>/dev/null || true
  done
fi

echo "[loader] 移交安装向导…"
SN_ARGS=()
[ -n "$SN" ] && SN_ARGS=(--sn "$SN")
bash "$STAGE/install.sh" "${SN_ARGS[@]}" "${ARGS[@]}"
LOADER_RC=$?

# ---- SN 更新代理植入（尽力而为，失败不影响安装结果） ----
# 探针 = cron 定时检查新版 + 写提醒标记；台账注册复用公开 check 接口
#（带有效 SN 才落库），端上不存任何管理令牌；一键升级走 $SN_BASE/update.sh。
if [ "$LOADER_RC" -eq  ]; then
  STATE_DIR="/var/lib/thinspider-update"
  CONF="/etc/thinspider-update.conf"
  mkdir -p "$STATE_DIR"
  [ -n "$SN" ] && printf '%s\n' "$SN" > "$STATE_DIR/sn"
  if [ ! -f "$CONF" ]; then
    {
      echo "# THINSPIDER SN 更新代理配置（安装时生成，可改）"
      echo "SN_BASE=\"$SN_BASE\""
    } > "$CONF"
    chmod 6 "$CONF"
  fi
  if curl -fsS --connect-timeout  --max-time 6 \
       -o "$STATE_DIR/update-probe.sh" \
       "$SN_BASE/update-probe.sh?product=THINSPIDER"; then
    sed -i 's/\r$//' "$STATE_DIR/update-probe.sh" 2>/dev/null || true
    chmod 7 "$STATE_DIR/update-probe.sh"
    CRON_LINE="*/3 * * * * root bash $STATE_DIR/update-probe.sh >/dev/null 2>&"
    CRON_F=/etc/cron.d/thinspider-update
    if [ -d /etc/cron.d ]; then
      grep -qx "$CRON_LINE" "$CRON_F" 2>/dev/null || printf '%s\n' "$CRON_LINE" > "$CRON_F"
      chmod 644 "$CRON_F"
    elif command -v crontab >/dev/null 2>&; then
      # 没有 /etc/cron.d 的发行版退到 root 用户 crontab
      ( crontab -l 2>/dev/null | grep -v 'update-probe.sh';
        echo "*/3 * * * * bash $STATE_DIR/update-probe.sh >/dev/null 2>&" ) | crontab -
    else
      echo "[loader] NOTE: 本机无 cron，更新代理未植入；可手工执行 $STATE_DIR/update-probe.sh 或在控制台看台账。"
    fi
    # 立刻跑一次：完成台账注册（不必等半个钟头）
    bash "$STATE_DIR/update-probe.sh" >/dev/null 2>& || true
    echo "[loader] SN 更新代理已植入（每 3 分钟检查新版；一键升级：curl -fsSL '$SN_BASE/update.sh?product=THINSPIDER' | bash）"
  else
    echo "[loader] NOTE: 更新代理拉取失败（不影响安装），可稍后在说明书页重新获取。"
  fi
fi

exit $LOADER_RC
