#!/bin/sh
set -eu

OCTO_RELEASE_VERSION='0.1.0'
OCTO_PLUGIN_VERSION='0.1.0+codex.20260822160748'
OCTO_RELEASE_PLATFORM='darwin'
OCTO_RELEASE_ARCH='arm64'
OCTO_ARTIFACT_NAME='octo-canvas-codex-marketplace-darwin-arm64-0.1.0'
OCTO_MARKETPLACE_NAME='xuanli-ai'
OCTO_EXPECTED_BYTES='58234481'
OCTO_EXPECTED_SHA256='ede905202dda36fbcfef7cc9edab0b3895f94749509883d6c0e83d85ccd22bb9'
OCTO_CANVAS_PLUGIN_ORIGIN=${OCTO_CANVAS_PLUGIN_ORIGIN:-'https://octo-canvas.xuanli.ai'}
OCTO_RELEASE_BASE="${OCTO_CANVAS_PLUGIN_ORIGIN%/}/releases/$OCTO_RELEASE_VERSION/$OCTO_RELEASE_PLATFORM-$OCTO_RELEASE_ARCH"

fail() {
  printf 'Octo Canvas 安装失败：%s\n' "$1" >&2
  exit 1
}

command -v curl >/dev/null 2>&1 || fail '缺少 curl。'
command -v tar >/dev/null 2>&1 || fail '缺少 tar。'
command -v codex >/dev/null 2>&1 || fail '没有找到 Codex CLI。请先安装或更新 Codex 桌面应用。'
codex plugin --help >/dev/null 2>&1 || fail '当前 Codex 版本不支持插件命令，请先更新 Codex。'

OCTO_SYSTEM=$(uname -s)
OCTO_MACHINE=$(uname -m)
if [ "$OCTO_SYSTEM" = 'Darwin' ] && [ "$OCTO_MACHINE" = 'x86_64' ]; then
  if [ "$(sysctl -in sysctl.proc_translated 2>/dev/null || true)" = '1' ]; then
    OCTO_MACHINE='arm64'
  fi
fi
if [ "$OCTO_SYSTEM" != 'Darwin' ] || [ "$OCTO_MACHINE" != 'arm64' ]; then
  fail "当前发布仅支持 macOS Apple Silicon，检测到 $OCTO_SYSTEM/$OCTO_MACHINE。"
fi

case "$OCTO_CANVAS_PLUGIN_ORIGIN" in
  https://*) ;;
  http://127.0.0.1:*|http://localhost:*) ;;
  *) fail '安装源必须使用 HTTPS；仅本机测试允许 loopback HTTP。' ;;
esac

OCTO_TMP_ROOT=$(mktemp -d "${TMPDIR:-/tmp}/octo-canvas-plugin-install.XXXXXX")
OCTO_PAYLOAD="$OCTO_TMP_ROOT/payload"
trap 'rm -rf "$OCTO_TMP_ROOT"' EXIT HUP INT TERM
: > "$OCTO_PAYLOAD"

hash_file() {
  if command -v shasum >/dev/null 2>&1; then
    shasum -a 256 "$1" | awk '{print $1}'
  elif command -v sha256sum >/dev/null 2>&1; then
    sha256sum "$1" | awk '{print $1}'
  else
    openssl dgst -sha256 "$1" | awk '{print $NF}'
  fi
}

download_chunk() {
  OCTO_CHUNK_NAME=$1
  OCTO_CHUNK_SHA256=$2
  OCTO_CHUNK_BYTES=$3
  OCTO_CHUNK_PATH="$OCTO_TMP_ROOT/$OCTO_CHUNK_NAME"
  printf '下载 %s...\n' "$OCTO_CHUNK_NAME"
  case "$OCTO_CANVAS_PLUGIN_ORIGIN" in
    https://*)
      curl --fail --location --silent --show-error --retry 3 \
        --proto '=https' --tlsv1.2 \
        "$OCTO_RELEASE_BASE/$OCTO_CHUNK_NAME" --output "$OCTO_CHUNK_PATH"
      ;;
    *)
      curl --fail --location --silent --show-error --retry 3 \
        "$OCTO_RELEASE_BASE/$OCTO_CHUNK_NAME" --output "$OCTO_CHUNK_PATH"
      ;;
  esac
  OCTO_ACTUAL_BYTES=$(wc -c < "$OCTO_CHUNK_PATH" | tr -d '[:space:]')
  [ "$OCTO_ACTUAL_BYTES" = "$OCTO_CHUNK_BYTES" ] || fail "$OCTO_CHUNK_NAME 文件大小校验失败。"
  OCTO_ACTUAL_SHA256=$(hash_file "$OCTO_CHUNK_PATH")
  [ "$OCTO_ACTUAL_SHA256" = "$OCTO_CHUNK_SHA256" ] || fail "$OCTO_CHUNK_NAME SHA-256 校验失败。"
  cat "$OCTO_CHUNK_PATH" >> "$OCTO_PAYLOAD"
  rm -f "$OCTO_CHUNK_PATH"
}

download_chunk 'payload.part-000' '8b77c8d5431f763d72ef5e5f634a12523fc07ffaeee8de542a5e9502519aa67b' '8388608'
download_chunk 'payload.part-001' 'a0028be850dc3b8a4367538944e57e55b03d77c0af0a5695acddd402eda6ff37' '8388608'
download_chunk 'payload.part-002' 'dc3fc2ca6eec2adb5c4e6bf508ec658e557bb29b6c9e6bf30287e1553de91bcf' '8388608'
download_chunk 'payload.part-003' '4a156377b6f2f61d30a500e742d0a3a73ef06d413948471c69cb31264d9fd700' '8388608'
download_chunk 'payload.part-004' 'b7d3dd7280548e6fe185528b03b64c69ec5fc3d8fd3a4ed80b53d08fd14b57dc' '8388608'
download_chunk 'payload.part-005' '492fa7747c5b6d882739128bbcf77c596aba039bb5b0e5873bd2783dd90f7cd8' '8388608'
download_chunk 'payload.part-006' 'a2c6346d4f085db47bfba1ff329f677705731c13373c37a0b564ba59b79d3149' '7902833'

OCTO_ACTUAL_BYTES=$(wc -c < "$OCTO_PAYLOAD" | tr -d '[:space:]')
[ "$OCTO_ACTUAL_BYTES" = "$OCTO_EXPECTED_BYTES" ] || fail '完整载荷文件大小校验失败。'
OCTO_ACTUAL_SHA256=$(hash_file "$OCTO_PAYLOAD")
[ "$OCTO_ACTUAL_SHA256" = "$OCTO_EXPECTED_SHA256" ] || fail '完整载荷 SHA-256 校验失败。'

tar -xzf "$OCTO_PAYLOAD" -C "$OCTO_TMP_ROOT"
OCTO_EXTRACTED_ROOT="$OCTO_TMP_ROOT/$OCTO_ARTIFACT_NAME"
OCTO_EXTRACTED_MARKETPLACE="$OCTO_EXTRACTED_ROOT/.agents/plugins/marketplace.json"
OCTO_EXTRACTED_PLUGIN="$OCTO_EXTRACTED_ROOT/plugins/octo-canvas/.codex-plugin/plugin.json"
[ -f "$OCTO_EXTRACTED_MARKETPLACE" ] || fail '安装载荷缺少 marketplace.json。'
[ -f "$OCTO_EXTRACTED_PLUGIN" ] || fail '安装载荷缺少 Octo Canvas manifest。'
grep -F '"name": "octo-canvas"' "$OCTO_EXTRACTED_PLUGIN" >/dev/null \
  || fail '插件名称校验失败。'
grep -F "\"version\": \"$OCTO_PLUGIN_VERSION\"" "$OCTO_EXTRACTED_PLUGIN" >/dev/null \
  || fail '插件版本校验失败。'
grep -F "\"name\": \"$OCTO_MARKETPLACE_NAME\"" "$OCTO_EXTRACTED_MARKETPLACE" >/dev/null \
  || fail 'Marketplace 名称校验失败。'

OCTO_INSTALL_BASE=${OCTO_CANVAS_PLUGIN_INSTALL_BASE:-"$HOME/.local/share/xuanli/octo-canvas-codex-plugin"}
case "$OCTO_INSTALL_BASE" in
  /*) ;;
  *) fail '插件安装目录必须是绝对路径。' ;;
esac
case "$OCTO_INSTALL_BASE" in
  /|/Users|/Applications|/Library|/System|/usr|/opt|/var|/tmp|"$HOME")
    fail '拒绝使用过宽的插件安装目录。'
    ;;
esac

OCTO_STABLE_ROOT="$OCTO_INSTALL_BASE/marketplace"
OCTO_STAGING_ROOT="$OCTO_INSTALL_BASE/.marketplace-next-$$"
OCTO_PREVIOUS_ROOT="$OCTO_INSTALL_BASE/.marketplace-previous-$$"
OCTO_FAILED_ROOT="$OCTO_INSTALL_BASE/.marketplace-failed-$$"
mkdir -p "$OCTO_INSTALL_BASE"
[ ! -e "$OCTO_STAGING_ROOT" ] || fail '暂存目录已存在，请稍后重试。'
[ ! -e "$OCTO_PREVIOUS_ROOT" ] || fail '回滚目录已存在，请稍后重试。'
[ ! -e "$OCTO_FAILED_ROOT" ] || fail '失败目录已存在，请稍后重试。'

safe_remove_tree() {
  case "$1" in
    "$OCTO_STAGING_ROOT"|"$OCTO_PREVIOUS_ROOT"|"$OCTO_FAILED_ROOT")
      rm -rf -- "$1"
      ;;
    *)
      fail '内部清理目标越过插件安装目录。'
      ;;
  esac
}

OCTO_HAD_PREVIOUS=0
mv "$OCTO_EXTRACTED_ROOT" "$OCTO_STAGING_ROOT"
if [ -e "$OCTO_STABLE_ROOT" ]; then
  mv "$OCTO_STABLE_ROOT" "$OCTO_PREVIOUS_ROOT"
  OCTO_HAD_PREVIOUS=1
fi
mv "$OCTO_STAGING_ROOT" "$OCTO_STABLE_ROOT"

rollback_marketplace() {
  if [ -e "$OCTO_STABLE_ROOT" ]; then
    mv "$OCTO_STABLE_ROOT" "$OCTO_FAILED_ROOT"
  fi
  if [ "$OCTO_HAD_PREVIOUS" = '1' ] && [ -e "$OCTO_PREVIOUS_ROOT" ]; then
    mv "$OCTO_PREVIOUS_ROOT" "$OCTO_STABLE_ROOT"
  fi
  [ ! -e "$OCTO_FAILED_ROOT" ] || safe_remove_tree "$OCTO_FAILED_ROOT"
}

if ! codex plugin marketplace add "$OCTO_STABLE_ROOT" --json \
  >"$OCTO_TMP_ROOT/marketplace-result.json" 2>"$OCTO_TMP_ROOT/marketplace-error.txt"; then
  rollback_marketplace
  sed -n '1,20p' "$OCTO_TMP_ROOT/marketplace-error.txt" >&2
  fail "无法添加 $OCTO_MARKETPLACE_NAME marketplace；如果同名源来自其他位置，请先人工确认。"
fi

if ! codex plugin add "octo-canvas@$OCTO_MARKETPLACE_NAME" --json \
  >"$OCTO_TMP_ROOT/plugin-result.json" 2>"$OCTO_TMP_ROOT/plugin-error.txt"; then
  if [ "$OCTO_HAD_PREVIOUS" = '1' ]; then
    rollback_marketplace
  fi
  sed -n '1,20p' "$OCTO_TMP_ROOT/plugin-error.txt" >&2
  fail 'Codex 没有完成插件安装。Marketplace 已保留，可在处理报错后重试。'
fi

grep -F '"pluginId": "octo-canvas@xuanli-ai"' "$OCTO_TMP_ROOT/plugin-result.json" >/dev/null \
  || fail 'Codex 返回的插件 ID 不符合预期。'
grep -F "\"version\": \"$OCTO_PLUGIN_VERSION\"" "$OCTO_TMP_ROOT/plugin-result.json" >/dev/null \
  || fail 'Codex 返回的插件版本不符合预期。'

if [ "$OCTO_HAD_PREVIOUS" = '1' ] && [ -e "$OCTO_PREVIOUS_ROOT" ]; then
  safe_remove_tree "$OCTO_PREVIOUS_ROOT"
fi

printf '\nOcto Canvas %s 已安装并启用。\n' "$OCTO_PLUGIN_VERSION"
printf '下一步：新建一个 Codex 任务，然后说「打开 Octo 画布」。\n'
printf '首次使用生成能力时，请登录你自己的即梦账号。\n'
