#!/usr/bin/env bash
#
# 发版:把「提交 → 打 tag → 推送 → 产出产物」串成一条命令。
#
# 用法:
#   ./scripts/release.sh 0.1.0                  # 发 0.1.0
#   ./scripts/release.sh 0.1.0 -m "提交说明"    # 顺带提交未保存的改动
#   ./scripts/release.sh 0.1.0 --yes            # 跳过中途确认
#   ./scripts/release.sh                        # 只报告当前状态,不动任何东西
#
# 版本号必须显式给出。major/minor/patch 该跳哪一位,取决于这次改动是不是
# 破坏性的(数据库 schema 变了没、配置格式变了没),机器判断不了——
# 所以本脚本不替你猜,只做校验。
#
# 注意:本脚本会产生远程副作用(push 到 origin)。tag 推上去之后再想撤很麻烦,
# 所以中途会列出完整计划等你确认,看清了再放行。
#
# 产物先生成在本地 publish/,随后自动上传到 gitcode 的 Release。
# 上传需要平台凭据(项目根的 atomgit_token,已被 .gitignore 忽略)。
# 没有凭据就跳过上传、只给出该去的地址——构建本身不该因为没配凭据而失败。
set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
REPO_URL="https://gitcode.com/hearkenundermoon/MMail"

VERSION=""
MESSAGE=""
ASSUME_YES=0

while [[ $# -gt 0 ]]; do
  case "$1" in
    -m|--message)
      MESSAGE="${2:-}"
      shift 2
      ;;
    --yes|-y)
      ASSUME_YES=1
      shift
      ;;
    -h|--help)
      sed -n '2,25p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
      exit 0
      ;;
    -*)
      echo "错误:未知参数 $1" >&2
      exit 1
      ;;
    *)
      if [[ -n "$VERSION" ]]; then
        echo "错误:版本号只能给一个(已有 $VERSION)" >&2
        exit 1
      fi
      VERSION="$1"
      shift
      ;;
  esac
done

cd "$ROOT"

if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "错误:不在 git 仓库中。" >&2
  exit 1
fi

if ! git remote get-url origin >/dev/null 2>&1; then
  echo "错误:未配置 origin,无法推送。" >&2
  exit 1
fi

BRANCH="$(git branch --show-current)"
LAST_TAG="$(git describe --tags --match 'v[0-9]*' --abbrev=0 2>/dev/null || true)"

if [[ -n "$LAST_TAG" ]]; then
  COMMITS_SINCE="$(git rev-list --count "${LAST_TAG}..HEAD")"
else
  COMMITS_SINCE="$(git rev-list --count HEAD)"
fi

# 没有版本号时只报告状态:这一步不产生任何改动,可以放心执行
if [[ -z "$VERSION" ]]; then
  echo "用法:$0 <版本号> [-m \"提交说明\"] [--yes]"
  echo ""
  echo "当前状态:"
  echo "  分支        : $BRANCH"
  echo "  最近的版本tag: ${LAST_TAG:-(还没有任何一个)}"
  echo "  该 tag 之后 : $COMMITS_SINCE 个提交"
  echo "  工作区      : $([[ -n "$(git status --porcelain)" ]] && echo '有未提交改动' || echo '干净')"
  echo ""
  echo "版本号需要你自己定:破坏性变更跳 major,新功能跳 minor,只修 bug 跳 patch。"
  exit 1
fi

if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
  echo "错误:版本号应为 x.y.z 形式(如 0.1.0),收到「$VERSION」。" >&2
  exit 1
fi

TAG="v$VERSION"

if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
  echo "错误:tag $TAG 在本地已存在。" >&2
  exit 1
fi

# 远端检查需要联网,失败只警告,不作为阻断条件
if git ls-remote --exit-code --tags origin "refs/tags/$TAG" >/dev/null 2>&1; then
  echo "错误:远端 origin 上已有 tag $TAG。" >&2
  exit 1
fi

DIRTY="$(git status --porcelain)"

if [[ -n "$DIRTY" && -z "$MESSAGE" ]]; then
  echo "错误:工作区有未提交改动,但没给 -m 提交说明。" >&2
  echo "      要么自己先提交干净,要么用:" >&2
  echo "        $0 $VERSION -m \"提交说明\"" >&2
  echo "" >&2
  echo "      待提交的内容:" >&2
  echo "$DIRTY" | head -20 >&2
  exit 1
fi

confirm() {
  if [[ "$ASSUME_YES" == "1" ]]; then
    echo "  $1 → 已用 --yes 跳过确认"
    return 0
  fi

  local reply=""
  read -r -p "  $1 [y/N] " reply || true
  [[ "$reply" == "y" || "$reply" == "Y" ]]
}

echo "==> 发版计划"
echo "    版本号    : $VERSION(tag $TAG)"
echo "    分支      : $BRANCH"
echo "    上一个 tag: ${LAST_TAG:-无}"
echo "    推送目标  : $(git remote get-url origin)"
echo "    提交      : $([[ -n "$DIRTY" ]] && echo "有未提交改动,将一并提交(说明:$MESSAGE)" || echo '无(工作区干净)')"
echo "    产物      : publish/(执行完 publish.sh --release 后生成)"

if [[ "$BRANCH" != "main" && "$BRANCH" != "master" ]]; then
  echo ""
  echo "    注意:当前不在 main/master 分支上,而是在 $BRANCH。"
fi

echo ""
if ! confirm "确认按上述计划发版?"; then
  echo "已取消,未做任何改动。"
  exit 0
fi

if [[ -n "$DIRTY" ]]; then
  echo ""
  echo "==> 提交改动"
  git add -A
  git commit -q -m "$MESSAGE"
  echo "    已提交:$(git log -1 --format=%s)"
fi

echo ""
echo "==> 打 tag $TAG"
git tag -a "$TAG" -m "$TAG"

echo ""
echo "==> 推送到 origin"

if ! git push origin "$BRANCH"; then
  echo "" >&2
  echo "错误:分支推送失败。tag $TAG 已在本地创建,但没有推上去。" >&2
  echo "      解决后手动执行:git push origin $TAG" >&2
  echo "      或撤销这个 tag:git tag -d $TAG" >&2
  exit 1
fi

if ! git push origin "$TAG"; then
  echo "" >&2
  echo "错误:tag 推送失败(分支已推上去)。" >&2
  echo "      解决后手动执行:git push origin $TAG" >&2
  echo "      或撤销这个 tag:git tag -d $TAG && git push origin --delete $TAG" >&2
  exit 1
fi

echo ""
echo "==> 生成产物"
# 到这里工作区必然干净、HEAD 上必然有 tag,publish.sh 的防呆一定过得去;
# 万一还是失败了,tag 已经推上去,得给出补救办法
if ! "$ROOT/scripts/publish.sh" --release; then
  echo "" >&2
  echo "错误:产物生成失败,但 tag $TAG 已经推上去了。" >&2
  echo "      代码与 tag 都是好的,修好构建问题后重新执行即可:" >&2
  echo "        ./scripts/release.sh $(( ${VERSION%%.*} )).x.x  # 换一个新版本号" >&2
  echo "      或者只是想重出产物(不打新 tag):" >&2
  echo "        git checkout $TAG && ./scripts/publish.sh --release" >&2
  exit 1
fi

echo ""
echo "==> 上传产物到 Release"

TOKEN_FILE="$ROOT/atomgit_token"

if [[ ! -f "$TOKEN_FILE" ]]; then
  # 缺凭据不算发版失败:代码、tag、本地产物都是好的,只是还没对外发布
  echo ""
  echo "    跳过:没找到凭据文件 atomgit_token。"
  echo "    Release 还没建,产物也没上传,需要手工做:"
  echo ""
  echo "      $REPO_URL/releases/new?tag=$TAG"
  echo ""
  echo "    把 token 放进项目根的 atomgit_token 之后,可以只补上传这一步:"
  echo "      ./scripts/upload-release.py $TAG"
  echo ""
  echo "==> 发版完成:$TAG(产物未上传)"
  exit 0
fi

if python3 "$ROOT/scripts/upload-release.py" "$TAG"; then
  echo ""
  echo "==> 发版完成:$TAG"
  echo "    $REPO_URL/releases/tag/$TAG"
  echo "    已部署实例的设置页点「检查更新」就能看到这个版本。"
else
  echo "" >&2
  echo "错误:产物上传失败。但代码、tag、本地产物都是好的,只需补传:" >&2
  echo "      ./scripts/upload-release.py $TAG" >&2
  exit 1
fi