#!/bin/bash
#
# Sublime Text 4 一鍵設定（macOS）
# 說明：https://oawu.tw/blog/macos/sublime-text-4/
#
# 用法：
#   curl -fsSL -o /tmp/sublime-install.sh https://oawu.tw/blog/macos/sublime-text-4/files/install.sh
#   bash /tmp/sublime-install.sh
#
# ── 會做的事 ─────────────────────────────────────
#   1. 安裝 /Applications/Sublime Text.app（從官網下載，不需要 Homebrew；已安裝則略過）
#   2. 安裝 Hack 字型到 ~/Library/Fonts/（已安裝則略過）
#   3. 若 Sublime Text 正在執行，先將它關閉（更動 Installed Packages 前必須關閉）
#   4. 安裝 Package Control 4.x 到 Installed Packages/（已有 4.x 以上則略過）
#   5. 下載 OA Theme.sublime-package 到 Installed Packages/
#   6. 下載 Default (OSX).sublime-keymap、Preferences.sublime-settings 到 Packages/User/
#   7. 把 PACKAGES 列出的套件併入 Package Control.sublime-settings，保留原有套件
#   8. 建立 ~/bin/subl，並在 ~/.zshrc 結尾加一行 PATH（已有則略過）
#   9. 啟動 Sublime Text，等待 Package Control 裝完套件（最多 PACKAGE_TIMEOUT 秒）
#
#   路徑皆在 ~/Library/Application Support/Sublime Text/ 底下。
#   設定檔內容不同時，舊檔先改名成 *.bak.<時間> 再覆蓋，不會直接蓋掉。
#   Sublime Text 啟動後會自行改寫 Preferences，所以重跑時 Preferences 會再備份一次，屬正常現象。
#
# ── 需要人工的步驟 ───────────────────────────────
#   通常沒有。以下兩種情況會以結束代碼 2 結束，處理後再執行一次本腳本：
#   - Sublime Text 無法關閉：多半是有未存檔的分頁跳出確認視窗
#   - 套件在時限內沒有裝完：Package Control 仍在背景下載，稍候重跑即可
#
# ── 結束代碼 ─────────────────────────────────────
#   0  完成
#   1  失敗（下載失敗等），訊息會說明原因
#   2  需要人工步驟或稍候，處理後重新執行本腳本
#
# ── 可重複執行 ───────────────────────────────────
#   已完成的步驟會略過，重跑是安全的。
#
# ── 其他 ─────────────────────────────────────────
#   設定檔從 BASE_URL 下載。測試時可用環境變數指到別處，例如本機的開發伺服器：
#     BASE_URL=http://localhost:4321/blog/macos/sublime-text-4/files bash install.sh
#

set -euo pipefail

BASE_URL="${BASE_URL:-https://oawu.tw/blog/macos/sublime-text-4/files}"

# 要安裝的套件
PACKAGES=(
  "SublimeLinter"
  "Sass"
  "A File Icon"
  "Git blame"
  "Swift"
  "Golang"
)

APP="/Applications/Sublime Text.app"
ST_DIR="$HOME/Library/Application Support/Sublime Text"
INSTALLED_DIR="$ST_DIR/Installed Packages"
USER_DIR="$ST_DIR/Packages/User"
BIN_DIR="$HOME/bin"
ZSHRC="$HOME/.zshrc"
PATH_LINE='export PATH="$PATH:$HOME/bin"'
PACKAGE_TIMEOUT=180

TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$TMP_DIR"' EXIT

# 沒有終端機（例如由其他程式代為執行）時不上色，輸出才不會夾雜控制碼
if [ -t 1 ]; then
  BOLD=$'\033[1m' BLUE=$'\033[1;34m' GREEN=$'\033[32m' GRAY=$'\033[90m' YELLOW=$'\033[33m' RED=$'\033[31m' RESET=$'\033[0m'
else
  BOLD='' BLUE='' GREEN='' GRAY='' YELLOW='' RED='' RESET=''
fi
# 下載進度條同理，沒有終端機時改成安靜模式（錯誤訊息仍會顯示）
if [ -t 2 ]; then PROGRESS='-#'; else PROGRESS='-sS'; fi
step() { printf '\n%s==>%s %s%s%s\n' "$BLUE" "$RESET" "$BOLD" "$1" "$RESET"; }
ok()   { printf '    %s✔%s %s\n' "$GREEN" "$RESET" "$1"; }
skip() { printf '    %s- %s%s\n' "$GRAY" "$1" "$RESET"; }
warn() { printf '    %s!%s %s\n' "$YELLOW" "$RESET" "$1"; }
fail() { printf '\n%s✘ %s%s\n' "$RED" "$1" "$RESET" >&2; exit 1; }

# 需要人工處理的步驟：說明要做什麼後以結束代碼 2 結束，做完再重跑。
# 不用 read 等待按鍵，因為由其他程式代為執行時沒有終端機可讀
manual() {
  printf '\n%s需要人工處理：%s\n' "$YELLOW" "$RESET"
  printf '    %s\n' "$@"
  printf '\n完成後重新執行本腳本，已完成的步驟會自動略過。\n'
  exit 2
}

# 從 BASE_URL 下載設定檔，輸出暫存檔路徑
fetch() {
  local name="$1" out="$TMP_DIR/$1"
  curl -fsSL "$BASE_URL/$(printf '%s' "$name" | sed 's/ /%20/g')" -o "$out" || fail "下載失敗：$BASE_URL/$name"
  printf '%s' "$out"
}

# 放置檔案：內容相同就略過，不同則先備份再覆蓋
place() {
  local src="$1" dest="$2" label
  label="$(basename "$dest")"
  if [ -f "$dest" ] && cmp -s "$src" "$dest"; then
    skip "$label 已是最新"
    return
  fi
  if [ -f "$dest" ]; then
    local bak="$dest.bak.$(date +%Y%m%d%H%M%S)"
    mv "$dest" "$bak"
    warn "已備份舊檔：$(basename "$bak")"
  fi
  cp "$src" "$dest"
  ok "$label"
}

is_running() { pgrep -x sublime_text >/dev/null 2>&1; }

[ "$(uname)" = "Darwin" ] || fail "此腳本只支援 macOS"

# 1. 安裝 Sublime Text
step "安裝 Sublime Text"
if [ -d "$APP" ]; then
  skip "已安裝"
else
  build="$(curl -fsSL https://www.sublimetext.com/updates/4/stable_update_check | sed -n 's/.*"latest_version": *\([0-9]*\).*/\1/p')"
  [ -n "$build" ] || fail "無法取得 Sublime Text 最新版本"
  curl -fL "$PROGRESS" "https://download.sublimetext.com/sublime_text_build_${build}_mac.zip" -o "$TMP_DIR/st.zip"
  ditto -x -k "$TMP_DIR/st.zip" "$TMP_DIR/st"
  mv "$TMP_DIR/st/Sublime Text.app" "$APP"
  ok "Sublime Text build $build"
fi

# 2. Hack 字型（Preferences 使用）
step "安裝 Hack 字型"
if [ -f "$HOME/Library/Fonts/Hack-Regular.ttf" ] || [ -f /Library/Fonts/Hack-Regular.ttf ]; then
  skip "已安裝"
else
  curl -fL "$PROGRESS" https://github.com/source-foundry/Hack/releases/latest/download/Hack-v3.003-ttf.zip -o "$TMP_DIR/hack.zip"
  ditto -x -k "$TMP_DIR/hack.zip" "$TMP_DIR/hack"
  mkdir -p "$HOME/Library/Fonts"
  find "$TMP_DIR/hack" -name '*.ttf' -exec cp {} "$HOME/Library/Fonts/" \;
  ok "Hack"
fi

# 更動 Installed Packages 前須關閉 Sublime Text
if is_running; then
  step "關閉 Sublime Text"
  osascript -e 'quit app "Sublime Text"'
  for _ in $(seq 1 20); do is_running || break; sleep 0.5; done
  is_running && manual "Sublime Text 無法關閉，多半是有未存檔的分頁跳出確認視窗。" \
                       "請存檔或放棄變更並關閉 Sublime Text。"
  ok "已關閉"
fi

mkdir -p "$INSTALLED_DIR" "$USER_DIR"

# 3. Package Control（需 4.x 以上，3.x 在新版 Sublime 不會載入）
step "安裝 Package Control"
pc="$INSTALLED_DIR/Package Control.sublime-package"
pc_ver="$(unzip -p "$pc" package-metadata.json 2>/dev/null | sed -n 's/.*"version": *"\([0-9]*\).*/\1/p' || true)"
if [ -n "$pc_ver" ] && [ "$pc_ver" -ge 4 ]; then
  skip "已安裝"
else
  curl -fsSL https://github.com/sublimehq/package_control/releases/latest/download/Package.Control.sublime-package -o "$TMP_DIR/pc.sublime-package" || fail "Package Control 下載失敗"
  place "$TMP_DIR/pc.sublime-package" "$pc"
fi

# 4. 主題
step "安裝 OA Theme"
f="$(fetch "OA Theme.sublime-package")"
place "$f" "$INSTALLED_DIR/OA Theme.sublime-package"

# 5. 快捷鍵
step "設定快捷鍵"
f="$(fetch "Default (OSX).sublime-keymap")"
place "$f" "$USER_DIR/Default (OSX).sublime-keymap"

# 6. 環境設定
step "設定 Preferences"
f="$(fetch "Preferences.sublime-settings")"
place "$f" "$USER_DIR/Preferences.sublime-settings"

# 7. 套件清單：併入 Package Control 設定，保留原有套件
step "設定套件清單"
pc_settings="$USER_DIR/Package Control.sublime-settings"
osascript -l JavaScript - "$pc_settings" "$TMP_DIR/pc.sublime-settings" "${PACKAGES[@]}" <<'JS' >/dev/null
ObjC.import('Foundation')
function run(argv) {
  var src = argv[0], out = argv[1], obj = {}
  if ($.NSFileManager.defaultManager.fileExistsAtPath(src)) {
    // Sublime 設定檔允許 // 註解與結尾逗號，先移除再解析
    var text = $.NSString.stringWithContentsOfFileEncodingError(src, $.NSUTF8StringEncoding, null).js
    obj = JSON.parse(text.replace(/^\s*\/\/.*$/gm, '').replace(/,(\s*[\]}])/g, '$1'))
  }
  var list = obj.installed_packages || []
  argv.slice(2).concat(['Package Control']).forEach(function (p) { if (list.indexOf(p) < 0) list.push(p) })
  obj.installed_packages = list.sort()
  $(JSON.stringify(obj, null, '\t') + '\n').writeToFileAtomicallyEncodingError(out, true, $.NSUTF8StringEncoding, null)
}
JS
place "$TMP_DIR/pc.sublime-settings" "$pc_settings"

# 8. 命令列 subl
step "設定 subl 指令"
mkdir -p "$BIN_DIR"
ln -sfn "$APP/Contents/SharedSupport/bin/subl" "$BIN_DIR/subl"
ok "$BIN_DIR/subl"
touch "$ZSHRC"
if grep -qxF "$PATH_LINE" "$ZSHRC"; then
  skip "~/.zshrc 已包含 ~/bin"
else
  printf '\n# My bin files\n%s\n' "$PATH_LINE" >> "$ZSHRC"
  ok "~/.zshrc 加入 ~/bin"
fi

# 9. 啟動 Sublime Text，讓 Package Control 安裝套件
step "安裝套件（等待 Package Control，最多 ${PACKAGE_TIMEOUT} 秒）"
missing() {
  local p
  for p in "${PACKAGES[@]}"; do
    [ -f "$INSTALLED_DIR/$p.sublime-package" ] || [ -d "$ST_DIR/Packages/$p" ] || printf '%s\n' "$p"
  done
}
if [ -z "$(missing)" ]; then
  skip "套件皆已安裝"
else
  open -a "$APP"
  list="$(IFS=,; echo "${PACKAGES[*]}")"
  nudged=0
  for i in $(seq 1 "$PACKAGE_TIMEOUT"); do
    [ -z "$(missing)" ] && break
    if [ "$nudged" = 0 ] && [ "$i" -ge 10 ]; then
      "$BIN_DIR/subl" --command "advanced_install_package {\"packages\": \"$list\"}" </dev/null >/dev/null 2>&1 || true
      nudged=1
    fi
    sleep 1
  done
  left="$(missing)"
  if [ -z "$left" ]; then
    ok "${PACKAGES[*]}"
  else
    warn "尚未安裝完成的套件："
    printf '%s\n' "$left" | sed 's/^/      /'
    manual "以上套件在 ${PACKAGE_TIMEOUT} 秒內沒有裝完，Package Control 可能仍在背景下載。" \
           "稍候一兩分鐘，或重新啟動 Sublime Text。"
  fi
fi

step "完成！"
echo "    新開終端機分頁後即可使用 subl 指令"
