#!/bin/bash
# Stamp the release version into every file that carries it.
#
# The git tag is the single source of truth for a release version. The versions
# committed in package.json / tauri.conf.json / Cargo.toml are a placeholder for
# dev builds; a tagged build overwrites all of them from the tag so they cannot
# disagree with each other or with the tag.
#
# Usage:
#   ./scripts/set-version.sh 0.5.0        # explicit
#   JELLYTAU_VERSION=0.5.0 ./scripts/set-version.sh
#   ./scripts/set-version.sh              # derive from git describe (dev builds)
#
# Accepts the version with or without a leading "v".
#
# Why a script and not four sed lines in CI: the version lived in four files and
# CI only ever rewrote one of them (tauri.conf.json), so a tagged release shipped
# a matching installer name and mismatched package metadata. Keeping the write in
# one place is what makes "the tag is authoritative" actually true.

set -euo pipefail

cd "$(dirname "$0")/.."

VERSION="${1:-${JELLYTAU_VERSION:-}}"

# CI passes "${GITHUB_REF#refs/tags/}" unconditionally, which on an untagged
# build is still a full ref ("refs/heads/master"). Treat anything that is not a
# bare version as "no version given" and fall through to git describe, so a
# branch build gets a sane dev version instead of failing the job.
case "$VERSION" in
  refs/*) VERSION="" ;;
esac

if [ -z "$VERSION" ]; then
  # No explicit version: derive from the most recent tag. Dev builds land on
  # something like 0.5.0 (exact tag) or 0.5.0-3-gabc1234 (ahead of the tag).
  VERSION="$(git describe --tags --always --match 'v*' 2>/dev/null || echo "0.0.0")"
fi

# Tags are written v0.5.0; the files carry a bare semver.
VERSION="${VERSION#v}"

# Validate before writing anything — a malformed version silently propagated
# into four files is far worse than a failed script.
if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$'; then
  echo "❌ Not a valid semver: '$VERSION'" >&2
  echo "   Expected MAJOR.MINOR.PATCH with an optional -prerelease/+build suffix." >&2
  exit 1
fi

echo "📌 Setting version to $VERSION"

# --- The three committed manifests -----------------------------------------
# Anchored to the first "version" key so a dependency's version is never hit.

# package.json — the top-level "version", which sits in the first few lines.
perl -0pi -e 's/("version"\s*:\s*)"[^"]*"/$1"'"$VERSION"'"/' package.json

# tauri.conf.json — likewise; this is the one the bundler reads for installer
# names, and the one CI used to patch alone.
perl -0pi -e 's/("version"\s*:\s*)"[^"]*"/$1"'"$VERSION"'"/' src-tauri/tauri.conf.json

# Cargo.toml — only the [package] version, never a dependency's. Restricted to
# the first occurrence of a line-anchored `version = "..."`.
perl -0pi -e 's/^(version\s*=\s*)"[^"]*"/$1"'"$VERSION"'"/m' src-tauri/Cargo.toml

# Cargo.lock — the jellytau entry. Left alone if the lock has not been generated
# yet; the next cargo invocation writes it. Cargo would otherwise rewrite the
# lock mid-build and dirty the tree.
if [ -f src-tauri/Cargo.lock ]; then
  perl -0pi -e 's/(name = "jellytau"\nversion = )"[^"]*"/$1"'"$VERSION"'"/' src-tauri/Cargo.lock
fi

# --- Android versionCode ----------------------------------------------------
# Only when the generated Android project exists (i.e. after `tauri android
# init`); on Linux/Windows jobs there is nothing to stamp.
#
# `tauri android init` derives a versionCode from the semver (0.0.15 -> 15).
# That is both tiny and NOT monotonic across our history: earlier local/dev
# builds shipped versionCode 1000 (from a 0.1.0 config), so a plain 15 is a
# *downgrade* and Android refuses the update.
#
# The floor has to clear the highest code actually in the field, which is not the
# same as the highest this formula has produced. v0.5.2 shipped versionCode
# **5002** under an earlier `minor*1000` scheme; the `minor*100` formula that
# replaced it yields only 1502 for that same version, and 1503 for 0.5.3 — so
# every 0.5.x release built from it was an un-installable downgrade for anyone
# already on v0.5.2, which is exactly the failure this block exists to prevent.
# The multipliers are widened and the floor raised past 5002 accordingly.
#
#   code = 10000 + major*1000000 + minor*1000 + patch
# e.g. 0.0.14 -> 10014, 0.1.0 -> 11000, 0.5.3 -> 15003, 1.0.0 -> 1010000.
PROPS="src-tauri/gen/android/app/tauri.properties"
if [ -f "$PROPS" ]; then
  # Strip any -rc1/+build suffix first: it is not numeric, and feeding it to
  # $(( )) would abort the script under `set -e`.
  CORE="${VERSION%%-*}"
  CORE="${CORE%%+*}"
  MAJ=$(echo "$CORE" | cut -d. -f1)
  MIN=$(echo "$CORE" | cut -d. -f2)
  PAT=$(echo "$CORE" | cut -d. -f3)
  : "${MAJ:=0}" "${MIN:=0}" "${PAT:=0}"
  CODE=$(( 10000 + MAJ*1000000 + MIN*1000 + PAT ))
  echo "   versionCode=$CODE (from $CORE)"
  if grep -q '^tauri.android.versionCode=' "$PROPS"; then
    sed -i "s/^tauri.android.versionCode=.*/tauri.android.versionCode=$CODE/" "$PROPS"
  else
    echo "tauri.android.versionCode=$CODE" >> "$PROPS"
  fi
fi

# --- Report -----------------------------------------------------------------
echo "✅ Version stamped:"
grep -m1 '"version"' package.json | sed 's/^/   package.json:      /'
grep -m1 '"version"' src-tauri/tauri.conf.json | sed 's/^/   tauri.conf.json:   /'
grep -m1 '^version' src-tauri/Cargo.toml | sed 's/^/   Cargo.toml:        /'
[ -f "$PROPS" ] && grep '^tauri.android.versionCode=' "$PROPS" | sed 's/^/   tauri.properties:  /'
exit 0
