#!/usr/bin/env bash
# sweep-stale-branches.sh —— 找到并清理已合并 / 已远端删除 / 长期不动的本地分支
#
# 默认 dry-run。加 --apply 才真删。
#
# 检测两类目标：
#   1. "gone" 分支：本地 tracking ref 显示远端已删（PR squash merge 后远端自动删，
#      或人工删）。这是最可靠的"可删"信号——squash merge 不会让本地 commit 成为
#      develop 的祖先，所以 git branch --merged 永远检测不到，必须用 gone。
#      **但 gone ≠ 安全可删**：删除前必须再过一道 absorbed 守门，确认内容已被任一
#      protected base（develop/staging/production）吸收。否则会误删 force-push 抹掉
#      远端 / 远端误删 / 本地有未推送 commit 的分支。复用 agent-pool 的
#      ap_ref_absorbed（L1 ancestor / L2 累积 patch-id / L3 per-commit patch-id）。
#   2. stale 分支：超过 STALE_DAYS（默认 30）天没有新 commit。**只报告不删**，
#      作者自己看着办（可能还在做、只是慢；或忘了，需手动确认）。
#
# 保护分支永不删，硬编码白名单。
#
# 用法:
#   bash scripts/dev/sweep-stale-branches.sh             # dry-run，看看
#   bash scripts/dev/sweep-stale-branches.sh --apply     # 真删 gone 分支
#   STALE_DAYS=14 bash scripts/dev/sweep-stale-branches.sh   # 改 stale 阈值

set -eo pipefail

# 复用 agent-pool lib 的 ap_ref_absorbed —— 3 层判定（ancestor / 累积 patch-id /
# per-commit patch-id），squash merge 后仍能正确识别"内容已被 develop 吸收"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck disable=SC1091
source "${SCRIPT_DIR}/lib/agent-pool-lib.sh"

PROTECTED_EXACT=(develop staging production main master)
# pool/ 前缀属 agent-pool park 分支保留命名空间（pool/slot-N 等），永不 sweep
# —— 这类分支 upstream 可能指向已 gone 的 feature 分支（历史 claim 残留），
# 会被误识别为 gone；即便误删 release 时会重建，仍属噪音，统一在白名单挡掉
PROTECTED_PREFIX=("chore/notes-rolling" "chore/rolling-" "pool/")
STALE_DAYS="${STALE_DAYS:-30}"
APPLY=false

for arg in "$@"; do
  case "${arg}" in
    --apply) APPLY=true ;;
    -h|--help)
      sed -n '2,/^$/p' "$0" | sed 's/^# \?//'
      exit 0
      ;;
    *) echo "unknown arg: ${arg}" >&2; exit 1 ;;
  esac
done

is_protected() {
  local b="$1"
  local p
  for p in "${PROTECTED_EXACT[@]}"; do
    [[ "${b}" == "${p}" ]] && return 0
  done
  for p in "${PROTECTED_PREFIX[@]}"; do
    [[ "${b}" == "${p}"* ]] && return 0
  done
  return 1
}

echo "=== git fetch --all --prune ==="
git fetch --all --prune --quiet

echo ""
echo "=== gone branches（远端已删，本地可清）==="
gone_count=0
deleted_count=0
skipped_count=0
while read -r branch; do
  [[ -z "${branch}" ]] && continue
  is_protected "${branch}" && continue
  gone_count=$((gone_count + 1))
  last_date="$(git log -1 --format='%ad' --date=short "${branch}" 2>/dev/null || echo '?')"
  echo "  ${branch}  (last: ${last_date})"
  if [[ "${APPLY}" == "true" ]]; then
    # absorbed 守门：内容必须已合到 develop/staging/production 才能删。
    # 防 force-push 抹掉远端 / 远端误删 / 本地未推送 commit 等场景误删丢工作。
    if ! ap_ref_absorbed "$(pwd)" "refs/heads/${branch}" 2>/dev/null; then
      echo "    🚫 skip: 内容未被任一 protected base 吸收，跳过删除"
      echo "         可能场景: force-push 抹掉远端 / 远端误删 / 本地有未推送 commit"
      echo "         人工确认后强删: git branch -D ${branch}"
      skipped_count=$((skipped_count + 1))
      continue
    fi
    if git branch -D "${branch}" >/dev/null 2>&1; then
      echo "    ✅ deleted"
      deleted_count=$((deleted_count + 1))
    else
      echo "    ⚠️  failed (in use? checked out?)"
    fi
  fi
done < <(
  # 用 for-each-ref 而非 `git branch -vv`：后者对被别的 worktree 检出的分支
  # 前缀 `+`（而非空格/`*`），awk $1 会误取到 `+`，留下幽灵分支
  git for-each-ref --format='%(refname:short) %(upstream:track)' refs/heads/ \
    | awk '/\[gone\]/ {print $1}'
)

if [[ ${gone_count} -eq 0 ]]; then
  echo "  (none)"
fi

echo ""
echo "=== stale branches（>${STALE_DAYS}d 未动，仅报告）==="
threshold=$(( $(date +%s) - STALE_DAYS * 86400 ))
stale_count=0
while read -r b; do
  is_protected "${b}" && continue
  ts="$(git log -1 --format='%ct' "${b}" 2>/dev/null || echo 0)"
  if [[ -n "${ts}" && "${ts}" -lt "${threshold}" ]]; then
    # 跳过已被识别为 gone 的（上面已列）。用 for-each-ref 避免 `git branch -vv` 的
    # `+` 前缀解析问题（被别的 worktree 检出的分支前缀是 `+`，正则匹配会漏）
    upstream_track="$(git for-each-ref --format='%(upstream:track)' "refs/heads/${b}" 2>/dev/null)"
    [[ "${upstream_track}" == *"[gone]"* ]] && continue
    last_date="$(git log -1 --format='%ad' --date=short "${b}" 2>/dev/null || echo '?')"
    echo "  ${b}  (last: ${last_date})"
    stale_count=$((stale_count + 1))
  fi
done < <(git for-each-ref --format='%(refname:short)' refs/heads/)

if [[ ${stale_count} -eq 0 ]]; then
  echo "  (none)"
fi

echo ""
if [[ "${APPLY}" == "true" ]]; then
  echo "✅ 已删除 ${deleted_count} 个 gone 分支。"
  if [[ ${skipped_count} -gt 0 ]]; then
    echo "🚫 跳过 ${skipped_count} 个 gone 分支（未被 base 吸收，见上方人工确认建议）。"
  fi
  echo "stale 分支需手动确认（可能还在做、未提交）。"
else
  echo "💡 dry-run 模式。要真删 gone 分支跑：bash $0 --apply"
fi
