#!/usr/bin/env bash
# scripts/local-deploy.sh — 本機 deploy 取代 GH Actions
#
# 對應 LI SDD M5.6 / 2026-05-16 leo GH Actions 被停用後的 fallback
#
# 用法：
#   bash scripts/local-deploy.sh              # 偵測 git diff，deploy 改到的 worker
#   bash scripts/local-deploy.sh --all        # 全部 worker deploy
#   bash scripts/local-deploy.sh cypher-executor registry  # 指定 worker
#   bash scripts/local-deploy.sh --base HEAD~3     # 改 diff base（預設 HEAD~1）
#   bash scripts/local-deploy.sh --dry-run    # 只 list 不 deploy
#
# 邏輯模擬 .github/workflows/deploy.yml discover job
#   - 掃所有 wrangler.toml 目錄
#   - git diff base..HEAD 找改動
#   - 對改到的 worker 跑 pnpm exec wrangler deploy
#   - 若改到 registry/components/{name}/，連動 deploy .component-builds/{name}/
#
# 要求：
#   - 已 wrangler login（pnpm exec wrangler whoami 確認）
#   - 在 arcrun/ 根目錄執行

set -euo pipefail

ARCRUN_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ARCRUN_ROOT"

# ── Parse args ────────────────────────────────────────────────────────────────

BASE_REF="HEAD~1"
DRY_RUN=false
DEPLOY_ALL=false
EXPLICIT_TARGETS=()

while [[ $# -gt 0 ]]; do
  case "$1" in
    --all) DEPLOY_ALL=true; shift ;;
    --base) BASE_REF="$2"; shift 2 ;;
    --dry-run) DRY_RUN=true; shift ;;
    -h|--help)
      sed -n '2,20p' "$0"
      exit 0
      ;;
    *) EXPLICIT_TARGETS+=("$1"); shift ;;
  esac
done

# ── 1. 列所有可 deploy 的 worker 目錄 ─────────────────────────────────────────
# 排除 node_modules、wrangler.test.toml、Pages 專案
echo "🔍 Scanning worker directories..."

# bash 3.2（macOS 內建）沒有 mapfile → 用 while read 相容寫法
ALL_DIRS=()
while IFS= read -r d; do
  [ -n "$d" ] && ALL_DIRS+=("$d")
done < <(
  find . -type f -name 'wrangler.toml' \
    -not -path '*/node_modules/*' \
    -not -name 'wrangler.test.toml' \
    | while read f; do
        if grep -q 'pages_build_output_dir' "$f"; then continue; fi
        dirname "$f"
      done \
    | sort -u \
    | sed 's|^\./||'
)

echo "Found ${#ALL_DIRS[@]} worker dirs"

# ── 2. 決定 targets ──────────────────────────────────────────────────────────

declare -a TARGETS=()

if [[ ${#EXPLICIT_TARGETS[@]} -gt 0 ]]; then
  echo "🎯 Explicit targets: ${EXPLICIT_TARGETS[*]}"
  for req in "${EXPLICIT_TARGETS[@]}"; do
    matched=false
    for d in "${ALL_DIRS[@]}"; do
      if [[ "$d" == "$req" || "$(basename "$d")" == "$req" ]]; then
        TARGETS+=("$d")
        matched=true
      fi
    done
    if [[ "$matched" == false ]]; then
      echo "⚠️  '$req' not found in worker list (跳過)"
    fi
  done
elif [[ "$DEPLOY_ALL" == true ]]; then
  echo "🎯 Mode: deploy all"
  TARGETS=("${ALL_DIRS[@]}")
else
  # git diff 過濾
  echo "🎯 Mode: git diff $BASE_REF..HEAD"

  if ! git rev-parse "$BASE_REF" >/dev/null 2>&1; then
    echo "❌ base ref '$BASE_REF' not found. 改用 --all 或 --base <valid-ref>"
    exit 1
  fi

  CHANGED=()
  while IFS= read -r f; do
    [ -n "$f" ] && CHANGED+=("$f")
  done < <(git diff --name-only "$BASE_REF" HEAD)
  echo "Changed files (${#CHANGED[@]}):"
  for f in ${CHANGED[@]+"${CHANGED[@]}"}; do echo "  $f"; done

  for d in "${ALL_DIRS[@]}"; do
    hit=0
    for f in ${CHANGED[@]+"${CHANGED[@]}"}; do
      if [[ "$f" == "$d"/* ]]; then hit=1; break; fi
    done
    # 連動：改 registry/components/{name}/ 也要 deploy .component-builds/{name}/
    if [[ $hit -eq 0 && "$d" == .component-builds/* ]]; then
      name="${d#.component-builds/}"
      for f in ${CHANGED[@]+"${CHANGED[@]}"}; do
        if [[ "$f" == "registry/components/$name"/* ]]; then hit=1; break; fi
      done
    fi
    if [[ $hit -eq 1 ]]; then TARGETS+=("$d"); fi
  done
fi

if [[ ${#TARGETS[@]} -eq 0 ]]; then
  echo "✨ Nothing to deploy. All up-to-date."
  exit 0
fi

# ── 3. 分 tier (component builds 先，orchestration 後) ────────────────────────

declare -a TIER1=() TIER2=()
for t in "${TARGETS[@]}"; do
  if [[ "$t" == .component-builds/* ]]; then
    TIER1+=("$t")
  else
    TIER2+=("$t")
  fi
done

echo ""
echo "📦 Deploy plan:"
echo "  Tier 1 (components, ${#TIER1[@]}):"
for t in ${TIER1[@]+"${TIER1[@]}"}; do echo "    - $t"; done
echo "  Tier 2 (orchestration, ${#TIER2[@]}):"
for t in ${TIER2[@]+"${TIER2[@]}"}; do echo "    - $t"; done
echo ""

if [[ "$DRY_RUN" == true ]]; then
  echo "(dry-run，不實際 deploy)"
  exit 0
fi

# ── 3.5 Git 同步前置閘（壓測階段 6 教訓：deploy-from-committed）────────────────
# self-hosted `acr init` 從 origin/main codeload 抓 worker 源。若本機領先 main 未 push，
# 部署 prod 用的是新碼但 self-hosted 用戶抓到舊碼 → 薄殼打不存在的 API（seed 404）。
# ∴ 實際 deploy 前強制 git 同步檢查；未過用 --skip-git-check 可強制略過（自負風險）。
if [[ "${SKIP_GIT_CHECK:-false}" != "true" ]]; then
  echo ""
  echo "🔎 Git 同步前置檢查（deploy 前必過；要略過設 SKIP_GIT_CHECK=true）..."
  if ! bash scripts/check-release.sh >/tmp/arcrun-release-check.txt 2>&1; then
    grep -E "✗|⚠|領先|未 commit|Git 未同步" /tmp/arcrun-release-check.txt | sed 's/^/   /'
    echo "❌ Git 未同步 → 先 commit + git push 再 deploy（見 RELEASE-CHECKLIST.md）。"
    echo "   （確定要在未 push 狀態 deploy？ SKIP_GIT_CHECK=true bash scripts/local-deploy.sh ...）"
    exit 1
  fi
  echo "  ✓ Git 已同步，繼續 deploy"
fi

# ── 4. wrangler whoami 健康檢查 ──────────────────────────────────────────────

# 隨便挑一個 worker dir 跑 whoami（pnpm 需要在 npm package 內）
SAMPLE_DIR=""
for d in "${TARGETS[@]}"; do
  if [[ -f "$d/package.json" ]]; then SAMPLE_DIR="$d"; break; fi
done
if [[ -z "$SAMPLE_DIR" ]]; then
  echo "❌ 找不到任何 target 有 package.json (wrangler whoami 需要)"
  exit 1
fi
echo "🔑 wrangler whoami:"
(cd "$SAMPLE_DIR" && pnpm exec wrangler whoami 2>&1 | grep -E "logged in|email" | head -2) || {
  echo "❌ wrangler 沒登入。先跑：cd $SAMPLE_DIR && pnpm exec wrangler login"
  exit 1
}
echo ""

# ── 5. 依序 deploy ──────────────────────────────────────────────────────────

deploy_one() {
  local dir="$1"
  echo ""
  echo "▶ Deploying $dir ..."
  pushd "$dir" >/dev/null

  # 若是 component build，先確認有 component.wasm (從 registry build 出來)
  if [[ "$dir" == .component-builds/* ]]; then
    name="${dir#.component-builds/}"
    if [[ ! -f "component.wasm" ]] && [[ -d "../../registry/components/$name" ]]; then
      echo "  ⚙️  rebuild WASM from registry/components/$name ..."
      (cd "../../registry/components/$name" && tinygo build -target=wasi -o "$name.wasm" main.go && cp "$name.wasm" "../../../.component-builds/$name/component.wasm") || {
        echo "  ❌ TinyGo build failed for $name"
        popd >/dev/null
        return 1
      }
    fi
  fi

  # pnpm install 若沒 node_modules（首次）
  if [[ ! -d "node_modules" ]] && [[ -f "package.json" ]]; then
    echo "  📥 pnpm install (first time)..."
    pnpm install --frozen-lockfile 2>&1 | tail -3
  fi

  if pnpm exec wrangler deploy 2>&1 | tail -4; then
    echo "  ✅ Done"
  else
    echo "  ❌ Failed"
    popd >/dev/null
    return 1
  fi
  popd >/dev/null
}

FAILED=()
for d in ${TIER1[@]+"${TIER1[@]}"}; do
  deploy_one "$d" || FAILED+=("$d")
done
for d in ${TIER2[@]+"${TIER2[@]}"}; do
  deploy_one "$d" || FAILED+=("$d")
done

echo ""
echo "════════════════════════════════════════════════════════════"
if [[ ${#FAILED[@]} -eq 0 ]]; then
  echo "✅ All ${#TARGETS[@]} workers deployed successfully"
else
  echo "⚠️  ${#FAILED[@]}/${#TARGETS[@]} workers failed:"
  for f in ${FAILED[@]+"${FAILED[@]}"}; do echo "  ❌ $f"; done
fi

# ── 6. CLI npm publish（壓測報告第 3 點：deploy 不只推 worker，npm CLI 也要同步）──
# 「推送 = 全部 publish target 到位」（tests/release.feature）。worker 走 wrangler，
# CLI 走 npm。只有 cli/ 在本次 diff 內 + 版本比 npm 新時才 publish（同版跳過，不假失敗）。
# 不在 --dry-run 時跑。需 npm login（npm whoami 確認）。
if [[ "${DRY_RUN:-false}" != "true" ]]; then
  CLI_CHANGED=false
  for t in "${TARGETS[@]:-}"; do [[ "$t" == "cli" || "$t" == "cli/" ]] && CLI_CHANGED=true; done
  # --all 或 diff 含 cli/ 都算
  if git diff --name-only "${BASE_REF}"..HEAD 2>/dev/null | grep -q '^cli/'; then CLI_CHANGED=true; fi
  if [[ "${DEPLOY_ALL:-false}" == "true" ]]; then CLI_CHANGED=true; fi

  if [[ "$CLI_CHANGED" == "true" ]]; then
    echo ""
    echo "▶ CLI npm publish ..."
    LOCAL_V=$(node -p "require('./cli/package.json').version" 2>/dev/null || echo "?")
    REMOTE_V=$(npm view arcrun version 2>/dev/null || echo "none")

    # 自動昇版（richblack：deploy 時自動 bump，避免忘了改）：
    #   若本機版本 == npm 上版本（= 改了 cli 但沒 bump）→ 自動 patch +1。
    #   留版本記錄：把本次 commit subject 寫進 cli/CHANGELOG.md。
    if [[ "$LOCAL_V" == "$REMOTE_V" ]]; then
      echo "  · 版本未 bump（$LOCAL_V 已在 npm），自動 patch +1 ..."
      NEW_V=$(cd cli && npm version patch --no-git-tag-version 2>/dev/null | tr -d 'v')
      LOCAL_V="$NEW_V"
      # CHANGELOG：prepend 新版本 + 本次 commit subject（無 commit 則標 manual deploy）
      COMMIT_SUBJ=$(git log -1 --format='%s' 2>/dev/null || echo 'manual deploy')
      COMMIT_DATE=$(git log -1 --format='%ad' --date=short 2>/dev/null || echo '')
      CHANGELOG="cli/CHANGELOG.md"
      TMP_CL=$(mktemp)
      {
        echo "# arcrun CLI Changelog"
        echo ""
        echo "## $NEW_V — $COMMIT_DATE"
        echo "- $COMMIT_SUBJ"
        echo ""
        if [[ -f "$CHANGELOG" ]]; then tail -n +2 "$CHANGELOG"; fi
      } > "$TMP_CL"
      mv "$TMP_CL" "$CHANGELOG"
      echo "  · 已 bump → ${NEW_V}，並記錄進 ${CHANGELOG}（記得 commit 這兩個檔）"
    fi

    # 優先用 .env 的 NPM_API_TOKEN（authToken）——互動 npm login 常因 publish 政策 403。
    # 無 token 才退回 npm whoami（互動登入）。token 不入 log。
    NPM_TOK=""
    if [[ -f .env ]]; then
      NPM_TOK=$(grep '^NPM_API_TOKEN=' .env 2>/dev/null | head -1 | cut -d= -f2- | tr -d '"'"'"' \r\n')
    fi
    PUB_RC=""
    if [[ -n "$NPM_TOK" ]]; then
      PUB_RC=$(mktemp)
      printf '//registry.npmjs.org/:_authToken=%s\n' "$NPM_TOK" > "$PUB_RC"
    fi
    if [[ -z "$NPM_TOK" ]] && ! npm whoami >/dev/null 2>&1; then
      echo "  ⚠ 無 .env NPM_API_TOKEN 且未 npm login，跳過 publish。手動：cd cli && npm publish"
      FAILED+=("cli:npm-publish(未登入)")
    else
      echo "  📦 publish arcrun $REMOTE_V → $LOCAL_V ..."
      if (cd cli && npm run build >/dev/null 2>&1 && \
          NPM_CONFIG_USERCONFIG="${PUB_RC:-$HOME/.npmrc}" npm publish --access public 2>&1 | tail -3); then
        echo "  ✅ npm publish 完成（arcrun@${LOCAL_V}）"
      else
        echo "  ❌ npm publish 失敗"
        FAILED+=("cli:npm-publish")
      fi
    fi
    [[ -n "$PUB_RC" ]] && rm -f "$PUB_RC"
  fi
fi

echo ""
echo "════════════════════════════════════════════════════════════"
if [[ ${#FAILED[@]} -eq 0 ]]; then
  echo "✅ 全部 publish target 到位（worker + 必要時 CLI npm）"
else
  echo "⚠️  ${#FAILED[@]} 項失敗（誠實回報，未假綠）："
  for f in ${FAILED[@]+"${FAILED[@]}"}; do echo "  ❌ $f"; done
  exit 1
fi
