#!/usr/bin/env bash
# Cut a release atomically: guard → check PyPI → bump → test → commit → tag.
#
# Prevents the version-drift / already-published traps:
#   * refuses if the working tree is dirty or you're not on main
#   * refuses if the target version is ALREADY on PyPI (immutable)
#   * keeps pyproject, __init__, the commit, and the tag perfectly in sync
#   * runs the test suite before committing
#
# Usage (from the cli/ dir):   ./scripts/release.sh 0.1.11
# Does NOT push — it prints the exact push commands for you to run.
set -euo pipefail

VERSION="${1:-}"
PKG="abom-cli"

red()   { printf '\033[31m%s\033[0m\n' "$*" >&2; }
green() { printf '\033[32m%s\033[0m\n' "$*"; }
info()  { printf '\033[36m%s\033[0m\n' "$*"; }

[ -n "$VERSION" ] || { red "usage: release.sh <version>   e.g. release.sh 0.1.11"; exit 2; }
echo "$VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$' \
  || { red "version must be X.Y.Z (got '$VERSION')"; exit 2; }

cd "$(dirname "$0")/.."          # -> cli/
ROOT="$(git rev-parse --show-toplevel)"

# 1. guards -----------------------------------------------------------------
[ -z "$(git status --porcelain)" ] \
  || { red "working tree is dirty — commit or stash first"; git status --short; exit 1; }

BRANCH="$(git rev-parse --abbrev-ref HEAD)"
[ "$BRANCH" = "main" ] || { red "not on main (on '$BRANCH')"; exit 1; }

git fetch origin "$BRANCH" -q || true
if [ -n "$(git log HEAD..origin/$BRANCH --oneline 2>/dev/null)" ]; then
  red "local main is behind origin/main — pull/rebase first"; exit 1
fi

if git rev-parse "v$VERSION" >/dev/null 2>&1; then
  red "tag v$VERSION already exists locally"; exit 1
fi

# 2. PyPI immutability check (the big one) -----------------------------------
info "checking PyPI for an existing ${PKG} ${VERSION}..."
if curl -fsS "https://pypi.org/pypi/$PKG/$VERSION/json" >/dev/null 2>&1; then
  red "$PKG $VERSION is ALREADY on PyPI — versions are immutable, pick a higher one"
  exit 1
fi
green "  ✓ $VERSION is free on PyPI"

# 3. bump version files ------------------------------------------------------
python3 - "$VERSION" <<'PY'
import re, sys
v = sys.argv[1]
for path, pat, repl in [
    ("pyproject.toml", r'^version = "[^"]+"', f'version = "{v}"'),
    ("src/abom/__init__.py", r'^__version__ = "[^"]+"', f'__version__ = "{v}"'),
]:
    s = open(path).read()
    s2 = re.sub(pat, repl, s, count=1, flags=re.M)
    if s == s2:
        raise SystemExit(f"could not bump version in {path}")
    open(path, "w").write(s2)
print(f"  bumped pyproject + __init__ -> {v}")
PY

# CHANGELOG: promote the "[Unreleased]" section to this version, keeping a
# fresh empty [Unreleased] above it. Done in python, not sed: the previous
# GNU-only "0,/re/" sed range was a silent no-op under BSD/macOS sed (exit 0,
# no substitution), which shipped two releases with an unrenamed CHANGELOG.
# This version verifies the edit happened and aborts the release if not.
CL="$ROOT/CHANGELOG.md"
if [ -f "$CL" ] && grep -q '^## \[Unreleased\]' "$CL"; then
  python3 - "$VERSION" "$CL" <<'PY'
import sys
version, path = sys.argv[1], sys.argv[2]
marker = "## [Unreleased]"
text = open(path).read()
updated = text.replace(marker, f"{marker}\n\n## [{version}]", 1)
if updated == text:
    raise SystemExit(f"CHANGELOG promotion failed: no '{marker}' replaced in {path}")
open(path, "w").write(updated)
PY
  green "  ✓ CHANGELOG [Unreleased] -> [$VERSION]"
fi

# 4. tests -------------------------------------------------------------------
info "running tests..."
python3 -m pytest tests/ -q || { red "tests failed — aborting (version files left bumped; revert with git checkout)"; exit 1; }

# 5. commit + tag ------------------------------------------------------------
git add pyproject.toml src/abom/__init__.py
[ -f "$CL" ] && git add "$CL"
git commit -q -m "Release v$VERSION"
git tag "v$VERSION"
green "  ✓ committed + tagged v$VERSION"

# 6. next steps (no push — you stay in control) ------------------------------
cat <<EOF

$(green "Release v$VERSION prepared.") To publish:

    git push origin main
    git push origin v$VERSION

The tag push triggers the PyPI release workflow.
EOF
