#!/usr/bin/env bash
# ============================================================
# 通用一键发布 extension 到阿里云 FC（persona 无关）
# 用法: publish.sh <extension目录> <persona根目录>
#   - <persona根目录>: daemon 按 session persona 解析后传入（.../personas/<id>）
#   - 平台凭证/脚本骨架/bootstrap 来自本脚本所在的共享 deploy-kit
#   - persona 注入值/占位符/特化逻辑/persona 密钥来自 <persona根>/extension-kit
# ============================================================
set -euo pipefail

KIT_DIR="$(cd "$(dirname "$0")/.." && pwd)"            # 共享 deploy-kit 根
EXT_DIR="${1:?用法: publish.sh <extension目录> <persona根目录>}"
EXT_DIR="$(cd "$EXT_DIR" && pwd)"
PERSONA_ROOT="${2:?缺 persona 根目录}"
PERSONA_KIT="$PERSONA_ROOT/extension-kit"

# 1) 配置：persona config.env（含平台值+注入值）+ extension 自身 ext.conf（覆盖）
[ -f "$PERSONA_KIT/config.env" ] && source "$PERSONA_KIT/config.env"
[ -f "$EXT_DIR/ext.conf" ] && source "$EXT_DIR/ext.conf"
APP_NAME="${APP_NAME:-$(basename "$EXT_DIR")}"
CODE_DIR="${CODE_DIR:-./server}"
FC_ENTRY="${FC_ENTRY:-dist/main.js}"
BUILD_CMD="${BUILD_CMD:-}"

# 全局唯一身份:APP_NAME (人读身份/表前缀根) + SLUG (4 位散列)。一次性 scaffold 时
# 由 new-extension.sh 锁定 + bake 进 ext.conf。这里的 fallback 兜底两种历史情况:
#   1) 老项目 (本改动前 scaffold) ext.conf 无 SLUG → 现场生成 + bake,保后续稳定
#   2) 手动 rm SLUG 或损坏 ext.conf → 同上,fail-safe
# 派生:
#   DEPLOY_NAME=${APP_NAME}-${SLUG}  → FC functionName/子域名/s 项目名 (DNS-safe 连字符)
#   表前缀=${APP_NAME}_${SLUG}_       → Supabase 表 (SQL-safe 下划线,见 remove-extension.sh)
if [ -z "${SLUG:-}" ]; then
  SLUG="$(openssl rand -hex 2)"
  printf '\nSLUG=%s\n' "$SLUG" >> "$EXT_DIR/ext.conf"
  echo "  📝 锁定 SLUG=$SLUG (含 4 位散列防同名冲突,已 bake 进 ext.conf)"
fi
DEPLOY_NAME="${APP_NAME}-${SLUG}"

# 2) 工具链 + 阿里云凭证（分层：shared demo + 可选 local override）
source "$KIT_DIR/scripts/ensure-toolchain.sh"
ensure_toolchain
[ -f "$KIT_DIR/.secrets/aliyun.env" ] || { echo "❌ 缺 $KIT_DIR/.secrets/aliyun.env（clawd ship 的共享 demo 凭证，OTA 自动刷新）。参考 .secrets/aliyun.env.example 填写。" >&2; exit 1; }
set -a; . "$KIT_DIR/.secrets/aliyun.env"; set +a
# aliyun.env.local: 本机个人 override（永远不被 OTA 覆盖，见 refreshDeployKit）。有就后 source 覆盖 env。
[ -f "$KIT_DIR/.secrets/aliyun.env.local" ] && { set -a; . "$KIT_DIR/.secrets/aliyun.env.local"; set +a; }
export ALIBABA_CLOUD_REGION_ID="${REGION:?config.env 缺 REGION}"
_ident="$(aliyun --mode AK sts GetCallerIdentity 2>&1)" || { echo "❌ aliyun sts GetCallerIdentity 失败(检查 .secrets/aliyun.env 凭证):" >&2; echo "$_ident" >&2; exit 1; }
ACC="$(printf '%s' "$_ident" | python3 -c 'import sys,json;print(json.load(sys.stdin)["AccountId"])')" || { echo "❌ 解析 AccountId 失败,aliyun 输出非 JSON:" >&2; echo "$_ident" >&2; exit 1; }
mkdir -p ~/.s
cat > ~/.s/access.yaml <<EOF
default:
  AccountID: '$ACC'
  AccessKeyID: $ALIBABA_CLOUD_ACCESS_KEY_ID
  AccessKeySecret: $ALIBABA_CLOUD_ACCESS_KEY_SECRET
EOF
chmod 600 ~/.s/access.yaml

# 3) persona hook（可选）：校验/导出 persona 特化运行时变量（如 SESSION_SECRET / APP_BASE_URL / persona 密钥）
if [ -f "$PERSONA_KIT/hooks/pre-deploy.sh" ]; then
  source "$PERSONA_KIT/hooks/pre-deploy.sh"
fi

# 4) build（框架相关）
echo "::stage::build"
if [ -n "$BUILD_CMD" ]; then
  echo "==> build: $BUILD_CMD"
  ( cd "$EXT_DIR" && eval "$BUILD_CMD" )
fi

# 5) bootstrap（共享，框架无关）
cp "$KIT_DIR/contract/bootstrap" "$EXT_DIR/$CODE_DIR/bootstrap"
chmod +x "$EXT_DIR/$CODE_DIR/bootstrap"

# 6) 通用占位符替换渲染 s.yaml：扫 persona s.yaml.tmpl 的所有 __X__，从 env 取同名变量
TMPL="$PERSONA_KIT/contract/s.yaml.tmpl"
[ -f "$TMPL" ] || { echo "❌ 缺 $TMPL" >&2; exit 1; }
sed_args=()
for ph in $(grep -oE '__[A-Z0-9_]+__' "$TMPL" | sort -u); do
  var="${ph#__}"; var="${var%__}"
  # declare -p 测「是否定义」（含空字符串），兼容 bash 3.2；未定义即 fail-loud
  if ! declare -p "$var" >/dev/null 2>&1; then
    echo "❌ s.yaml.tmpl 占位符 $ph 无对应 env 变量 \$$var（必填项缺失，或可选项未在 config.env/hook 里 export 兜底）" >&2
    exit 1
  fi
  # 转义 sed 替换串里的特殊字符（密钥/URL 可能含 \ | &），顺序：先 \ 再 | / &
  val="${!var}"
  val="${val//\\/\\\\}"
  val="${val//|/\\|}"
  val="${val//&/\\&}"
  sed_args+=(-e "s|$ph|$val|g")
done
sed "${sed_args[@]}" "$TMPL" > "$EXT_DIR/s.yaml"

# 7) deploy + verify
echo "::stage::deploy"
echo "==> s deploy ($DEPLOY_NAME)"
( cd "$EXT_DIR" && s deploy -y )

echo "::stage::verify"
# 精确匹配 s.yaml.tmpl 里写死的 domainName 模式: <DEPLOY_NAME>.app.clawos.chat
EXPECTED_DOM="${DEPLOY_NAME}.app.clawos.chat"
DOM="$(aliyun --mode AK fc GET /2023-03-30/custom-domains --region "$REGION" 2>/dev/null | python3 -c "
import sys,json
d=json.load(sys.stdin)
target='$EXPECTED_DOM'
m=[c['domainName'] for c in d.get('customDomains',[]) if c.get('domainName','')==target]
print(m[0] if m else '')")"

echo ""
echo "=================================================="
if [ -n "$DOM" ]; then
  echo "::prod-url::https://$DOM"
  echo "  ✅ 发布完成: https://$DOM"
  echo "=================================================="
  bash "$KIT_DIR/scripts/verify.sh" "https://$DOM"
else
  echo "❌ 部署完成但未取到自定义域名 $EXPECTED_DOM,检查 fc3-domain 是否成功 + DNS *.app.clawos.chat CNAME 是否生效" >&2
  exit 1
fi
