#!/bin/sh

# --- helpers ---

die() { printf '%s\n' "error: $*" >&2; exit 1; }

usage() {
    printf 'Usage: version-tag <version-file>\n'
    printf '\n'
    printf 'Create and push a git version tag derived from <version-file>.\n'
    printf '\n'
    printf 'Arguments:\n'
    printf '  <version-file>  path to the version file (e.g. VERSION)\n'
    printf '\n'
    printf 'The script verifies before tagging:\n'
    printf '  - no modified or staged files in the working tree\n'
    printf '  - no unpushed or unpulled commits (fetches remote first)\n'
    printf '  - the latest commit message references the version string\n'
    printf '  - the tag does not already exist\n'
    printf '\n'
    printf 'On success, creates and pushes tag "v<version>" to origin.\n'
}

case ${1-} in --help|-h) usage; exit 0 ;; esac

[ $# -ge 1 ] || { usage >&2; exit 2; }

# --- script ---

version_file="$1"

[ -f "$version_file" ] || die "version file not found: $version_file"
[ -r "$version_file" ] || die "version file not readable: $version_file"

version=$(cat "$version_file") || die "failed to read $version_file"
version=$(printf '%s' "$version" | tr -d '[:space:]')
[ -n "$version" ] || die "version file is empty: $version_file"

# Verify we're inside a git repository
git rev-parse --git-dir >/dev/null 2>&1 || die "not inside a git repository"

# 1. Verify no modified or staged files
dirty=$(git status --porcelain | grep -v '^??')
if [ -n "$dirty" ]; then
    printf 'error: working tree has uncommitted changes:\n' >&2
    printf '%s\n' "$dirty" >&2
    exit 1
fi

# 2. Verify an upstream tracking branch exists (required for sync checks)
git rev-parse --abbrev-ref '@{u}' >/dev/null 2>&1 \
    || die "no upstream tracking branch — cannot verify sync (run: git branch -u origin/<branch>)"

# 3. Fetch all tags and update remote-tracking refs
printf 'fetching from remote...\n'
git fetch --tags || die "git fetch --tags failed"

# 2+3. Verify no unpushed or unpulled commits (checked after fetch for accuracy)
unpushed=$(git log '@{u}..HEAD' --oneline)
if [ -n "$unpushed" ]; then
    printf 'error: unpushed commits exist — push before tagging:\n' >&2
    printf '%s\n' "$unpushed" >&2
    exit 1
fi

unpulled=$(git log 'HEAD..@{u}' --oneline)
if [ -n "$unpulled" ]; then
    printf 'error: unpulled commits exist — pull before tagging:\n' >&2
    printf '%s\n' "$unpulled" >&2
    exit 1
fi

# 4. Verify the last commit message contains the version string
last_msg=$(git log -1 --format='%s')
case "$last_msg" in
    *"$version"*) ;;
    *)
        die "last commit message does not reference version '${version}'
  commit: ${last_msg}
  hint:   commit the version bump first (e.g. pyproject-version-commit)"
        ;;
esac

# 5. Create and push the tag
tag="v${version}"

git rev-parse "$tag" >/dev/null 2>&1 \
    && die "tag '${tag}' already exists"

printf 'creating tag %s...\n' "$tag"
git tag "$tag" || die "failed to create tag $tag"

printf 'pushing tag %s to origin...\n' "$tag"
if ! git push origin "$tag"; then
    git tag -d "$tag"
    die "failed to push tag ${tag} — local tag removed"
fi

printf 'done: %s\n' "$tag"
