#!/bin/sh
# Versioning guardrail — runs the structural checks from
# docs/versioning.md that don't need git history. Designed to be
# called from a CI job (once we have one) and/or as a pre-commit
# step. Exits 0 if everything is in shape, non-zero on the first
# failure with a precise message.
#
# What this DOES check:
#   * Migration filenames are sequential `0001_*.sql`, `0002_*.sql`,
#     ... starting from 0001 with no gaps and no duplicates.
#   * SDK_VERSION in shared::version parses as MAJOR.MINOR (numeric).
#   * Workspace product version in Cargo.toml parses as
#     MAJOR.MINOR.PATCH (numeric).
#
# What this does NOT check (deferred until we have CI + a CHANGELOG
# file):
#   * Whether an SDK major bump was paired with a CHANGELOG entry.
#   * Whether commits that retype public fields carry a `BREAKING:`
#     annotation in the commit message.
#
# Usage: bash scripts/check-versioning.sh

set -eu

# Resolve repo root from this script's location so the checks run no
# matter what working directory the caller is in.
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"

fail() {
    printf 'check-versioning: FAIL — %s\n' "$1" >&2
    exit 1
}

# ----------------------------------------------------------------------
# 1. Migration filenames sequential
# ----------------------------------------------------------------------
MIGRATIONS_DIR="$REPO_ROOT/crates/manager-core/migrations"
[ -d "$MIGRATIONS_DIR" ] || fail "migrations dir not found at $MIGRATIONS_DIR"

i=1
for file in "$MIGRATIONS_DIR"/*.sql; do
    [ -e "$file" ] || fail "no migration files found in $MIGRATIONS_DIR"
    base="$(basename "$file")"
    expected_prefix="$(printf '%04d_' "$i")"
    case "$base" in
        "$expected_prefix"*)
            ;;
        *)
            fail "migration $base is not next-in-sequence (expected ${expected_prefix}<name>.sql); migrations must be added with strictly increasing 4-digit numbers"
            ;;
    esac
    i=$((i + 1))
done
printf 'check-versioning: OK — %d migration(s) numbered sequentially\n' "$((i - 1))"

# ----------------------------------------------------------------------
# 2. SDK_VERSION format
# ----------------------------------------------------------------------
SDK_FILE="$REPO_ROOT/crates/shared/src/version.rs"
[ -f "$SDK_FILE" ] || fail "shared::version not found at $SDK_FILE"

SDK_VERSION="$(
    awk '/^pub const SDK_VERSION/ { match($0, /"[^"]+"/); print substr($0, RSTART+1, RLENGTH-2); exit }' "$SDK_FILE"
)"
[ -n "$SDK_VERSION" ] || fail "could not parse SDK_VERSION from $SDK_FILE"

case "$SDK_VERSION" in
    [0-9]*"."[0-9]*)
        # Reject things like "1.2.3" or "v1.2" or empty parts.
        major="${SDK_VERSION%%.*}"
        minor="${SDK_VERSION#*.}"
        case "$major" in
            ''|*[!0-9]*) fail "SDK_VERSION '$SDK_VERSION' major is not numeric" ;;
        esac
        case "$minor" in
            ''|*[!0-9]*) fail "SDK_VERSION '$SDK_VERSION' minor is not numeric (extra components?)" ;;
        esac
        ;;
    *)
        fail "SDK_VERSION '$SDK_VERSION' is not MAJOR.MINOR (expected e.g. '1.1')"
        ;;
esac
printf 'check-versioning: OK — SDK_VERSION = %s\n' "$SDK_VERSION"

# ----------------------------------------------------------------------
# 3. Workspace product version (semver MAJOR.MINOR.PATCH)
# ----------------------------------------------------------------------
ROOT_CARGO="$REPO_ROOT/Cargo.toml"
[ -f "$ROOT_CARGO" ] || fail "workspace Cargo.toml not found"

PRODUCT_VERSION="$(
    awk '
        /^\[workspace\.package\]/ { in_section = 1; next }
        /^\[/                     { in_section = 0 }
        in_section && /^version *= */ {
            match($0, /"[^"]+"/)
            print substr($0, RSTART+1, RLENGTH-2)
            exit
        }
    ' "$ROOT_CARGO"
)"
[ -n "$PRODUCT_VERSION" ] || fail "could not parse [workspace.package].version from $ROOT_CARGO"

case "$PRODUCT_VERSION" in
    [0-9]*"."[0-9]*"."[0-9]*)
        major="${PRODUCT_VERSION%%.*}"
        rest="${PRODUCT_VERSION#*.}"
        minor="${rest%%.*}"
        patch="${rest#*.}"
        for part_name in major minor patch; do
            eval "part=\$$part_name"
            case "$part" in
                ''|*[!0-9]*)
                    fail "product version '$PRODUCT_VERSION' has non-numeric $part_name component"
                    ;;
            esac
        done
        ;;
    *)
        fail "product version '$PRODUCT_VERSION' is not MAJOR.MINOR.PATCH"
        ;;
esac
printf 'check-versioning: OK — product version = %s\n' "$PRODUCT_VERSION"

printf '\ncheck-versioning: all checks passed.\n'
