#!/bin/bash
# system-dev-template updater
# 已安裝舊版的人，一鍵更新到新版。
#
# 核心安全原則：只覆蓋「模板/邏輯檔」，絕不碰「使用者資料檔」。
#   ✅ 可覆蓋：hooks/*.sh、commands/*.md、TEMPLATE-*、wiki/INDEX.md
#              ——這些由模板維護，使用者不會手改，新版直接換掉。
#   🔒 絕不碰：wiki/status.md、mistakes.md、decisions-summary.md、TAXONOMY.md、.wikiignore、
#              settings.json、CLAUDE.md
#              ——這些是使用者自己填的內容，覆蓋＝清空他的記憶與設定。
#
# 「第一次更新」的雞生蛋問題：
#   舊版本機沒有 update.sh。所以第一次靠 README 那行 curl 從遠端抓這支腳本來跑。
#   跑完它會把自己也更新進 scripts/update.sh，之後就能直接跑本機的 `bash scripts/update.sh`。

set -euo pipefail

# ── i18n：依 locale 選語言，預設英文（curl | bash 常為 LANG=C）──
case "${LC_ALL:-${LC_MESSAGES:-${LANG:-}}}" in
  zh*|*Hant*|*Hans*) IS_ZH="yes" ;;
  *)                 IS_ZH="no" ;;
esac
t()  { if [ "$IS_ZH" = "yes" ]; then printf '%s\n' "$1"; else printf '%s\n' "$2"; fi; }
tn() { if [ "$IS_ZH" = "yes" ]; then printf '%s' "$1";  else printf '%s' "$2";  fi; }

REPO_RAW="https://git.uncle6.me/Leo/system-dev-template/raw/branch/main"
TEMPLATE_URL="$REPO_RAW/template"

UPDATED=()
KEPT=()
NEW=()
TEMPLATED=()
MIGRATED=()
COEXIST=()

# ── 版本比對：先看本機 vs 遠端，給使用者「值不值得更新」的判斷 ──
# VERSION 新位置在 system-dev/，舊位置在 .claude/（1.8.x 以前）。優先讀新、回退舊。
LOCAL_VER="$(tn '(未知)' '(unknown)')"
if [ -f "system-dev/VERSION" ]; then
  LOCAL_VER="$(tr -d '[:space:]' < system-dev/VERSION)"
elif [ -f ".claude/VERSION" ]; then
  LOCAL_VER="$(tr -d '[:space:]' < .claude/VERSION)"
fi
REMOTE_VER="$(curl -sSL "$TEMPLATE_URL/system-dev/VERSION" 2>/dev/null | tr -d '[:space:]' || echo '')"
# 容錯：curl 對 404 會把「404:NotFound」當內容輸出（非空），舊版誤把它寫進 VERSION。
# 這裡驗證必須像版號（X.Y.Z），否則一律視為取不到，避免污染 VERSION 檔。
case "$REMOTE_VER" in
  [0-9]*.[0-9]*.[0-9]*) : ;;   # 形如 1.9.0 → 合法
  *) REMOTE_VER="" ;;          # 404 / HTML 錯誤頁 / 其他 → 當作沒抓到
esac

echo ""
echo "🔄 system-dev-template updater"
echo "================================="
t "   本機版本：${LOCAL_VER}" "   Local version:  ${LOCAL_VER}"
t "   最新版本：${REMOTE_VER:-取不到（檢查網路）}" \
  "   Latest version: ${REMOTE_VER:-unavailable (check network)}"
echo ""

if [ -z "$REMOTE_VER" ]; then
  t "❌ 取不到遠端版本，可能是網路問題。請稍後再試。" \
    "❌ Could not fetch the remote version (likely a network issue). Please try again later."
  exit 1
fi

if [ "$LOCAL_VER" = "$REMOTE_VER" ]; then
  t "✅ 已是最新版（${LOCAL_VER}），不需更新。" \
    "✅ Already up to date (${LOCAL_VER}), nothing to update."
  t "   （仍會同步模板邏輯檔，確保 hooks/commands 與最新一致。）" \
    "   (Template logic files will still be synced to keep hooks/commands in line with the latest.)"
  echo ""
fi

# ── 結構遷移（1.9.0）：舊版把 wiki/VERSION 放 .claude/、工具 docs 放根 docs/ ──
# 新版一律收進 system-dev/。這裡冪等遷移：偵測舊位置 → 搬到 system-dev/，已搬過則略過。
# 必須在「模組偵測」之前跑（偵測靠目錄存在與否判斷，搬完才看得到新位置）。
#
# 安全原則：
#   - wiki 整包搬（含 cards/ 與可能的 wiki/.git），用 mv 保留內含 .git。
#   - docs 只搬「工具自己鋪的白名單」子目錄；用戶自填在 docs/ 的其他內容一律不動。
#   - 目的地已存在同名 → 不覆蓋（保留用戶在新位置的東西），略過該項。
migrate_dir() {  # $1=舊路徑 $2=新路徑
  local from="$1" to="$2"
  [ -e "$from" ] || return 0          # 舊的不存在 → 無需遷移
  if [ -e "$to" ]; then
    # 目的地已存在。兩種可能：
    #   (a) 已遷移過 → 舊位置不該還在；冪等略過即可。
    #   (b) 用戶先 install 建了空殼 → 舊位置仍有真資料，現在「並存」。
    # 不能靜默跳過 (b)，也絕不自動合併（覆蓋風險）。→ 記為「並存待合併」，警告。
    COEXIST+=("$from ↔ $to")
    return 0
  fi
  mkdir -p "$(dirname "$to")"
  if mv "$from" "$to" 2>/dev/null; then
    MIGRATED+=("$from → $to")
  fi
}

# 任一舊位置還在 → 需要遷移（遷移本身冪等：已搬的項目會被 migrate_dir 略過）。
NEEDS_MIGRATE="no"
if [ -d ".claude/wiki" ] || [ -f ".claude/VERSION" ] \
   || [ -d "docs/3-specs" ] || [ -f "docs/SKILL.md" ] || [ -f "docs/README.md" ]; then
  NEEDS_MIGRATE="yes"
fi

if [ "$NEEDS_MIGRATE" = "yes" ]; then
  t "🔧 偵測到舊版結構，遷移到 system-dev/ …" "🔧 Old layout detected — migrating into system-dev/ …"
  mkdir -p system-dev

  # wiki（含 cards/ 與內含的 .git）整包搬
  migrate_dir ".claude/wiki" "system-dev/wiki"
  # 工具版號
  migrate_dir ".claude/VERSION" "system-dev/VERSION"
  # 工具文件白名單（只搬工具鋪的，用戶自填的 docs 內容不動）
  migrate_dir "docs/SKILL.md"            "system-dev/docs/SKILL.md"
  migrate_dir "docs/README.md"           "system-dev/docs/README.md"
  migrate_dir "docs/1-vision"            "system-dev/docs/1-vision"
  migrate_dir "docs/2-architecture"      "system-dev/docs/2-architecture"
  migrate_dir "docs/3-specs"             "system-dev/docs/3-specs"
  migrate_dir "docs/4-guides"            "system-dev/docs/4-guides"
  migrate_dir "docs/5-records"           "system-dev/docs/5-records"
  migrate_dir "docs/6-user"             "system-dev/docs/6-user"
  echo ""
fi

# ── 工具函式 ───────────────────────────────────────
# 覆蓋更新：模板/邏輯檔，無條件抓最新版蓋掉。
update_file() {
  local dest="$1" src="$2"
  mkdir -p "$(dirname "$dest")"
  if [ -f "$dest" ]; then
    if curl -sSL "$src" -o "$dest.tmp" 2>/dev/null && [ -s "$dest.tmp" ]; then
      if cmp -s "$dest" "$dest.tmp"; then
        rm -f "$dest.tmp"          # 內容相同，不算更新
      else
        mv "$dest.tmp" "$dest"
        UPDATED+=("$dest")
      fi
    else
      rm -f "$dest.tmp"
      t "   ⚠️  抓取失敗，保留原檔：$dest" "   ⚠️  Download failed, keeping the original: $dest"
    fi
  else
    if curl -sSL "$src" -o "$dest" 2>/dev/null && [ -s "$dest" ]; then
      NEW+=("$dest")               # 新功能：舊版沒有的檔
    else
      rm -f "$dest"
      t "   ⚠️  抓取失敗：$dest" "   ⚠️  Download failed: $dest"
    fi
  fi
}

# 保留：使用者資料檔，只記錄「有保留」，永遠不動。
keep_file() {
  [ -f "$1" ] && KEPT+=("$1") || true
}

# 補新檔：舊版沒有、新版才有的「使用者資料檔」（如 principles.md）。
#   不存在 → 抓範本下來（之後由使用者/CC 填）；已存在 → 當用戶資料保留，絕不覆蓋。
add_if_missing() {
  local dest="$1" src="$2"
  if [ -f "$dest" ]; then
    KEPT+=("$dest")
  elif curl -sSL "$src" -o "$dest" 2>/dev/null && [ -s "$dest" ]; then
    NEW+=("$dest")
  else
    rm -f "$dest"
  fi
}

# 客製檔：使用者一定會手填內容（如 pre-write-guard.sh）。
#   - 已存在 → 絕不覆蓋，但把最新模板版抓到 <檔名>.template.sh 旁邊，供使用者自行 diff 採納。
#   - 不存在 → 視同新檔，直接抓本體（第一次安裝才會走這條）。
keep_with_template() {
  local dest="$1" src="$2"
  if [ -f "$dest" ]; then
    KEPT+=("$dest")
    local tmpl="${dest%.sh}.template.sh"
    if curl -sSL "$src" -o "$tmpl.tmp" 2>/dev/null && [ -s "$tmpl.tmp" ]; then
      if [ -f "$tmpl" ] && cmp -s "$tmpl" "$tmpl.tmp"; then
        rm -f "$tmpl.tmp"            # 模板版沒變，不重複提示
      else
        mv "$tmpl.tmp" "$tmpl"
        TEMPLATED+=("$tmpl")
      fi
    else
      rm -f "$tmpl.tmp"
    fi
  else
    update_file "$dest" "$src"        # 還沒裝過 → 當新檔處理
  fi
}

# ── 偵測已安裝哪些模組（依現有檔案判斷，更新只動已裝的）──
# 遷移已在上面跑完，這裡看新位置 system-dev/。
HAS_WIKI=false
HAS_SDD=false
[ -d "system-dev/wiki" ] && HAS_WIKI=true
if [ -f ".claude/hooks/sdd-guard.sh" ] || [ -d "system-dev/docs/3-specs/TEMPLATE-sdd" ]; then HAS_SDD=true; fi

t "📦 偵測到已安裝模組：" "📦 Detected installed modules:"
$HAS_WIKI && echo "   • LLM Wiki"
$HAS_SDD  && echo "   • SDD"
{ $HAS_WIKI || $HAS_SDD; } || \
  t "   （未偵測到任何模組——這裡可能還沒安裝，請改跑 install.sh）" \
    "   (No modules detected — nothing installed here yet; run install.sh instead.)"
echo ""

# ── 客製檔：使用者手填的 guardrail，永不覆蓋（issue #3）──
# pre-write-guard.sh 的定位是「空白客製模板，使用者沒配置前不提供保護」（CHANGELOG 1.2.0）。
# 下游通常已塞滿自己的 enforcement，直接覆蓋＝無聲關掉整套 guardrail。
# 改為：保留原檔不動，新版範本另存 pre-write-guard.template.sh，由使用者自行 diff 採納。
keep_with_template ".claude/hooks/pre-write-guard.sh" "$TEMPLATE_URL/.claude/hooks/pre-write-guard.sh"

# ── 模板/邏輯檔：覆蓋更新 ──────────────────────────
# 共用 hook 與指引
update_file ".claude/commands/issue-handle.md" "$TEMPLATE_URL/.claude/commands/issue-handle.md"
update_file "system-dev/VERSION"               "$TEMPLATE_URL/system-dev/VERSION"

# Logseq 任務 marker 解析（單一真相源）：vault 萃取（/wiki-extract）與 tasks→Project
#   投影共用同一套；任一模組在用就補/更新（邏輯檔，可覆蓋）。
if $HAS_WIKI || $HAS_SDD; then
  update_file "system-dev/docs/4-guides/logseq-markers.md" "$TEMPLATE_URL/system-dev/docs/4-guides/logseq-markers.md"
fi

if $HAS_WIKI; then
  # wiki 的「邏輯檔」：導航與 hooks，可覆蓋。wiki 資料在 system-dev/，hooks/commands 留 .claude/。
  update_file "system-dev/wiki/INDEX.md"                 "$TEMPLATE_URL/system-dev/wiki/INDEX.md"
  update_file ".claude/hooks/session-start-recall.sh"   "$TEMPLATE_URL/.claude/hooks/session-start-recall.sh"
  update_file ".claude/hooks/wiki-secret-scan.sh"       "$TEMPLATE_URL/.claude/hooks/wiki-secret-scan.sh"
  # 1.16.0：讓 wiki 真的被讀到的兩支（開場 push 全文解決不了「只讀開頭」，見 CHANGELOG）
  update_file ".claude/hooks/wiki-first-search.sh"      "$TEMPLATE_URL/.claude/hooks/wiki-first-search.sh"
  update_file ".claude/hooks/subagent-wiki-guard.sh"    "$TEMPLATE_URL/.claude/hooks/subagent-wiki-guard.sh"
  update_file ".claude/commands/wiki-init.md"           "$TEMPLATE_URL/.claude/commands/wiki-init.md"
  update_file ".claude/commands/wiki-capture.md"        "$TEMPLATE_URL/.claude/commands/wiki-capture.md"
  update_file ".claude/commands/wiki-update.md"         "$TEMPLATE_URL/.claude/commands/wiki-update.md"
  update_file ".claude/commands/wiki-recall.md"         "$TEMPLATE_URL/.claude/commands/wiki-recall.md"
  # vault 增量萃取（Logseq/Obsidian → system-dev/wiki，冪等）：邏輯檔，可覆蓋。舊版沒有 → 當新檔補。
  update_file ".claude/commands/wiki-extract.md"        "$TEMPLATE_URL/.claude/commands/wiki-extract.md"
  # Cowork（claude.ai）的 wiki 整理 skill：規則檔，可覆蓋
  update_file "system-dev/docs/SKILL.md"                "$TEMPLATE_URL/system-dev/docs/SKILL.md"

  # wiki 的「使用者資料」：絕不碰
  keep_file "system-dev/wiki/status.md"
  keep_file "system-dev/wiki/mistakes.md"
  # principles.md（1.10）：舊版沒有 → 補範本；已有 → 當用戶資料保留
  add_if_missing "system-dev/wiki/principles.md" "$TEMPLATE_URL/system-dev/wiki/principles.md"
  keep_file "system-dev/wiki/decisions-summary.md"
  keep_file "system-dev/wiki/TAXONOMY.md"
  keep_file "system-dev/wiki/.wikiignore"
fi

if $HAS_SDD; then
  # SDD 範本與 hook：可覆蓋
  update_file "system-dev/docs/3-specs/TEMPLATE-sdd/design.md"            "$TEMPLATE_URL/system-dev/docs/3-specs/TEMPLATE-sdd/design.md"
  update_file "system-dev/docs/3-specs/TEMPLATE-sdd/tasks.md"             "$TEMPLATE_URL/system-dev/docs/3-specs/TEMPLATE-sdd/tasks.md"
  update_file "system-dev/docs/2-architecture/decisions/TEMPLATE-adr.md"  "$TEMPLATE_URL/system-dev/docs/2-architecture/decisions/TEMPLATE-adr.md"
  update_file ".claude/commands/sdd-check.md"                             "$TEMPLATE_URL/.claude/commands/sdd-check.md"
  update_file ".claude/hooks/sdd-guard.sh"                                "$TEMPLATE_URL/.claude/hooks/sdd-guard.sh"

  # SDD 生命週期鐵律（1.14，issue #6）：規則檔＋獨立檢查腳本＝邏輯檔可覆蓋；
  # pending-changes.md 裝著用戶的 proposal＝用戶資料，只補不覆蓋。
  # （issue #13 教訓：update 不補新檔會造成結構斷層，新檔必須在這裡鋪。）
  update_file    "system-dev/docs/3-specs/SDD-LIFECYCLE.md"    "$TEMPLATE_URL/system-dev/docs/3-specs/SDD-LIFECYCLE.md"
  add_if_missing "system-dev/docs/3-specs/pending-changes.md"  "$TEMPLATE_URL/system-dev/docs/3-specs/pending-changes.md"
  update_file    "system-dev/scripts/sdd-active-check.sh"      "$TEMPLATE_URL/scripts/sdd-active-check.sh"

  # tasks⇄Project 投影（issue #16）：邏輯檔，可覆蓋。舊版沒有 → add_if_missing 補。
  # 啟用狀態存遠端（acr push），不在這些檔裡，覆蓋不會關掉誰的同步。
  add_if_missing "system-dev/workflows/tasks-project-sync.yaml"     "$TEMPLATE_URL/system-dev/workflows/tasks-project-sync.yaml"
  add_if_missing "system-dev/workflows/tasks-project-sync.local.sh" "$TEMPLATE_URL/system-dev/workflows/tasks-project-sync.local.sh"
fi

# ── 自我更新：把最新的 update.sh / install.sh 抓到 system-dev/scripts/ ──
# 這兩支在 main/scripts/ 下（不在 template/）；落地位置新版收進 system-dev/scripts/。
update_file "system-dev/scripts/update.sh"  "$REPO_RAW/scripts/update.sh"
update_file "system-dev/scripts/install.sh" "$REPO_RAW/scripts/install.sh"

chmod +x .claude/hooks/*.sh system-dev/scripts/*.sh system-dev/workflows/*.sh 2>/dev/null || true

# ── 使用者資料檔：絕不碰，但提醒「設定可能有新欄位要手動補」──
keep_file ".claude/settings.json"
keep_file "CLAUDE.md"

# ── 結果輸出 ───────────────────────────────────────
echo ""
echo "─────────────────────────────────"
if [ ${#MIGRATED[@]} -gt 0 ]; then
  echo ""
  t "📦 結構遷移（已收進 system-dev/）：" "📦 Layout migrated (moved into system-dev/):"
  for f in "${MIGRATED[@]}"; do echo "   ⇒ $f"; done
fi
if [ ${#COEXIST[@]} -gt 0 ]; then
  echo ""
  t "🛑 偵測到 wiki 並存（新舊位置都有資料，需要合併）：" \
    "🛑 Coexisting wiki detected (both old and new locations have data — needs merging):"
  for f in "${COEXIST[@]}"; do echo "   ↔ $f"; done
  t "   成因：先跑過 install（建了空殼）才遷移，舊位置真資料沒被搬。" \
    "   Cause: install ran first (created an empty shell), so migration skipped your real data in the old location."
  t "   不自動合併（避免覆蓋你的資料）。請叫你的 CC：" \
    "   Not auto-merged (to avoid overwriting your data). Ask your CC:"
  t "   「.claude/wiki/ 和 system-dev/wiki/ 並存，請逐檔比對、把真資料合進 system-dev/，再刪舊的」" \
    "   \"There are two wikis (.claude/wiki/ and system-dev/wiki/) — diff each file, merge the real data into system-dev/, then delete the old one.\""
fi
if [ ${#NEW[@]} -gt 0 ]; then
  echo ""
  t "🆕 新功能（舊版沒有，已加入）：" "🆕 New features (absent in the old version, now added):"
  for f in "${NEW[@]}"; do echo "   + $f"; done
fi
if [ ${#UPDATED[@]} -gt 0 ]; then
  echo ""
  t "⬆️  已更新（覆蓋成新版）：" "⬆️  Updated (overwritten with the new version):"
  for f in "${UPDATED[@]}"; do echo "   ~ $f"; done
fi
if [ ${#NEW[@]} -eq 0 ] && [ ${#UPDATED[@]} -eq 0 ]; then
  echo ""
  t "✨ 模板邏輯檔已全部最新，無需變動。" \
    "✨ All template logic files are already up to date — no changes needed."
fi
if [ ${#KEPT[@]} -gt 0 ]; then
  echo ""
  t "🔒 完整保留（你的內容/設定，從未碰過）：" \
    "🔒 Fully preserved (your content/settings, never touched):"
  for f in "${KEPT[@]}"; do echo "   = $f"; done
fi
if [ ${#TEMPLATED[@]} -gt 0 ]; then
  echo ""
  t "📋 客製檔有新版範本（你的原檔沒動，新版另存旁邊，請自行 diff 採納）：" \
    "📋 Custom files have a new template version (your original is untouched; the new one is saved alongside — diff and adopt as you like):"
  for f in "${TEMPLATED[@]}"; do
    echo "   → $f"
    t "      比對：diff \"${f%.template.sh}.sh\" \"$f\"" \
      "      compare: diff \"${f%.template.sh}.sh\" \"$f\""
  done
fi

# ── settings.json 提醒：新模組 hook 可能要手動補 ──
if [ -f ".claude/settings.json" ]; then
  MISSING=()
  $HAS_WIKI && ! grep -q "session-start-recall.sh" .claude/settings.json && MISSING+=("SessionStart: session-start-recall.sh")
  $HAS_WIKI && ! grep -q "wiki-secret-scan.sh"     .claude/settings.json && MISSING+=("PreToolUse(Write|Edit): wiki-secret-scan.sh")
  $HAS_SDD  && ! grep -q "sdd-guard.sh"            .claude/settings.json && MISSING+=("PreToolUse(Write|Edit): sdd-guard.sh")

  # ── 1.16.0：兩支 wiki 讀取 hook 自動註冊（不只提醒）──
  # 理由：這兩支的整個存在意義就是「不依賴任何人記得」。
  # 若靠人看提醒去手動補 settings.json，等於把同一個病搬到安裝環節。
  if $HAS_WIKI && command -v python3 >/dev/null 2>&1; then
    python3 - <<'PYEOF' 2>/dev/null || true
import json, os
p = ".claude/settings.json"
try:
    with open(p) as f: d = json.load(f)
except Exception:
    raise SystemExit(0)          # 壞掉的 settings 不碰，交給下方提醒
pre = d.setdefault("hooks", {}).setdefault("PreToolUse", [])
blob = json.dumps(pre)
added = []
# 1.16.1：既有註冊若漏 Bash（原版只掛 Grep|Glob|Read）就地補上——
# 破口實例：用 curl/wrangler 亂試部署方法走 Bash，整支 hook 不觸發。
for _e in pre:
    if "wiki-first-search" in json.dumps(_e) and "Bash" not in _e.get("matcher", ""):
        _e["matcher"] = "Grep|Glob|Read|Bash"; added.append("wiki-first-search(補Bash)")
if "wiki-first-search" not in blob:
    pre.append({"matcher": "Grep|Glob|Read|Bash", "hooks": [
        {"type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/wiki-first-search.sh"}]})
    added.append("wiki-first-search")
if "subagent-wiki-guard" not in blob:
    pre.append({"matcher": "Task", "hooks": [
        {"type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/subagent-wiki-guard.sh"}]})
    added.append("subagent-wiki-guard")
if added:
    with open(p, "w") as f: json.dump(d, f, ensure_ascii=False, indent=2)
    print("  ✅ 已自動註冊 wiki 讀取 hook：" + "、".join(added))
PYEOF
  fi
  if [ ${#MISSING[@]} -gt 0 ]; then
    echo ""
    t "📌 settings.json 是你的設定（沒動），但偵測到缺以下 hook，請手動補上：" \
      "📌 settings.json is yours (untouched), but these hooks are missing — please add them manually:"
    for h in "${MISSING[@]}"; do echo "   • $h"; done
  fi
fi

echo ""
t "🚀 更新完成：${LOCAL_VER} → ${REMOTE_VER}" "🚀 Update complete: ${LOCAL_VER} → ${REMOTE_VER}"
t "   下次更新直接跑：bash system-dev/scripts/update.sh" "   Next time, just run: bash system-dev/scripts/update.sh"
t "   改了什麼看：CHANGELOG.md" "   See what changed: CHANGELOG.md"
echo ""
