#!/bin/sh
# ==============================================================================
# 脚本名称: git-restore-all-remote-branches.sh
# 描述:     基于本地远程引用批量恢复本地跟踪分支
# 兼容性:   兼容所有 POSIX sh 环境，Git 1.7+
# ==============================================================================

set -e

# -------------------------- 变量定义 ----------------------------------------
DRY_RUN="false"
NO_FETCH="false"
PRUNE_REMOTE="false"
REMOTE_NAME=""
FETCH_FAIL_EXIT="false"

total_refs=0
would_create=0
created=0
failed=0
skipped=0
illegal_names=0

# -------------------------- 帮助函数 ----------------------------------------
usage() {
  cat <<EOF
用法: $(basename "$0") [选项]

选项:
  -h, --help            显示此帮助信息
  -n, --dry-run         预演模式：仅打印命令，不实际创建分支
  -f, --no-fetch        跳过 git fetch，直接使用本地缓存的远程引用
  -p, --prune           fetch时附带 --prune，清理僵尸远程引用
      --fetch-fail-exit fetch失败时直接终止脚本
  -r, --remote NAME     指定远程名称；不提供则自动检测

示例:
  $(basename "$0")                      # 自动检测远程，恢复所有跟踪分支
  $(basename "$0") --dry-run            # 预演，查看将要创建哪些分支
  $(basename "$0") --prune              # 同步远程并清理僵尸引用（推荐）
  $(basename "$0") -r upstream -f       # 指定remote，不执行fetch
EOF
  exit "${1:-0}"
}

# -------------------------- 参数解析 ----------------------------------------
while [ $# -gt 0 ]; do
  case "$1" in
    -h|--help)
      usage 0
      ;;
    -n|--dry-run)
      DRY_RUN="true"
      shift
      ;;
    -f|--no-fetch)
      NO_FETCH="true"
      shift
      ;;
    -p|--prune)
      PRUNE_REMOTE="true"
      shift
      ;;
    --fetch-fail-exit)
      FETCH_FAIL_EXIT="true"
      shift
      ;;
    -r|--remote)
      if [ -z "${2:-}" ]; then
        echo "错误: --remote/-r 必须提供远程名称" >&2
        usage 1
      fi
      REMOTE_NAME="$2"
      shift 2
      ;;
    *)
      echo "未知参数: $1" >&2
      usage 1
      ;;
  esac
done

# -------------------------- 自动检测远程名称 --------------------------------
if [ -z "${REMOTE_NAME}" ]; then
  REMOTE_NAME=$(git remote | head -n1)
  if [ -z "${REMOTE_NAME}" ]; then
    echo "错误: 当前仓库没有配置任何远程仓库" >&2
    exit 1
  fi
  echo "自动检测到远程: ${REMOTE_NAME}"
fi

# 校验remote是否真实存在
if ! git remote get-url "${REMOTE_NAME}" >/dev/null 2>&1; then
  echo "错误: 远程 '${REMOTE_NAME}' 不存在" >&2
  exit 1
fi

# -------------------------- Fetch同步远程引用 -------------------------------
if [ "${NO_FETCH}" != "true" ]; then
  echo "正在同步远程引用: git fetch ${REMOTE_NAME}"
  fetch_args="${REMOTE_NAME}"
  if [ "${PRUNE_REMOTE}" = "true" ]; then
    fetch_args="${fetch_args} --prune"
  fi

  if ! git fetch ${fetch_args} 2>&1; then
    echo "警告: git fetch 执行失败！" >&2
    echo "警告: 后续将使用本地过期缓存，分支列表可能不一致！" >&2
    if [ "${FETCH_FAIL_EXIT}" = "true" ]; then
      echo "错误: 用户指定 --fetch-fail-exit，脚本终止" >&2
      exit 1
    fi
  fi
fi

# -------------------------- 读取远程引用列表 --------------------------------
ref_list=$(git for-each-ref --format='%(refname)' "refs/remotes/${REMOTE_NAME}/" 2>/dev/null)

# 校验是否扫描到任何远程引用
if [ -z "${ref_list}" ]; then
  echo "警告: 远程 '${REMOTE_NAME}' 下没有任何远程引用" >&2
  echo "提示: 如果远端确实有分支，请检查网络后重试，或移除 --no-fetch 参数" >&2
  echo ""
  echo "===== 统计汇总 ====="
  echo "扫描总远程引用: 0"
  echo "========================"
  exit 0
fi

echo "开始扫描远程引用: refs/remotes/${REMOTE_NAME}/"

# -------------------------- 逐行处理远程引用 --------------------------------
# 【修复Bug2】使用 here-doc 替代 pipe，避免 while 在子shell中运行导致变量丢失
while IFS= read -r ref; do
  # 跳过空行
  if [ -z "${ref}" ]; then
    continue
  fi

  total_refs=$((total_refs + 1))

  # 从完整引用路径提取分支名
  branch="${ref#refs/remotes/${REMOTE_NAME}/}"

  # 跳过特殊HEAD引用
  case "${branch}" in
    HEAD)
      echo "跳过特殊引用 HEAD"
      skipped=$((skipped + 1))
      continue
      ;;
  esac

  # 校验本地分支是否已存在
  if git show-ref --verify --quiet "refs/heads/${branch}" 2>/dev/null; then
    echo "本地分支【${branch}】已存在，跳过"
    skipped=$((skipped + 1))
    continue
  fi

  # 使用 git check-ref-format 检测非法分支名
  if ! git check-ref-format --allow-onelevel "${branch}" >/dev/null 2>&1; then
    echo "警告: 分支【${branch}】名称不符合Git规范，跳过创建" >&2
    illegal_names=$((illegal_names + 1))
    skipped=$((skipped + 1))
    continue
  fi

  # 斜杠分支冲突告警
  case "${branch}" in
    */*)
      prefix="${branch%%/*}"
      if git show-ref --verify --quiet "refs/heads/${prefix}" 2>/dev/null; then
        echo "警告: 分支【${branch}】含斜杠，前缀【${prefix}】已是文件分支，可能创建失败"
      fi
      ;;
  esac

  # 预演模式
  if [ "${DRY_RUN}" = "true" ]; then
    would_create=$((would_create + 1))
    echo "[DRY-RUN] 准备创建: git branch --track ${branch} ${ref}"
    continue
  fi

  # 实际创建分支
  # 【修复Bug1】使用 if 判断退出码（兼容 set -e），避免 git branch 失败时脚本直接退出
  # git branch --track 成功时向 stdout 输出信息（如 "branch 'xxx' set up to track..."），
  # 失败时向 stderr 输出错误信息。必须检查退出码而非输出内容是否为空。
  if err_msg=$(git branch --track "${branch}" "${ref}" 2>&1); then
    created=$((created + 1))
    echo "分支【${branch}】创建成功"
  else
    failed=$((failed + 1))
    echo "分支【${branch}】创建失败" >&2
    # 根据错误信息分类打印根因
    case "${err_msg}" in
      *"already exists"*)
        echo "   原因: 本地分支已存在" >&2
        ;;
      *"not a valid ref"*)
        echo "   原因: 分支名不符合Git规范" >&2
        ;;
      *"exists"*)
        echo "   原因: Git ref文件/目录冲突（前缀分支已存在）" >&2
        ;;
      *"Permission denied"*|*"Authentication failed"*)
        echo "   原因: 权限或认证问题" >&2
        ;;
      *)
        echo "   详细错误: ${err_msg}" >&2
        ;;
    esac
  fi
done <<EOF
${ref_list}
EOF

# -------------------------- 统计汇总 ----------------------------------------
echo ""
echo "===== 统计汇总 ====="
echo "扫描总远程引用: ${total_refs}"
if [ "${DRY_RUN}" = "true" ]; then
  echo "预演-待创建分支: ${would_create}"
  echo "预演模式说明：仅模拟，不会实际创建分支"
else
  echo "成功创建分支: ${created}"
  echo "创建失败分支: ${failed}"
  echo "跳过分支(已存在/非法名/HEAD): ${skipped}"
  if [ "${illegal_names}" -gt 0 ]; then
    echo "  - 非法分支名: ${illegal_names}"
  fi
fi
echo "========================"

# 退出码
if [ "${DRY_RUN}" != "true" ] && [ "${failed}" -gt 0 ]; then
  exit 1
fi
exit 0