#!/usr/bin/env bash
# Build (and by default install) an Arch Linux package for BikeControl.
#
# Tauri's bundler has no pacman target, so we ship a hand-written PKGBUILD in
# packaging/arch/ and build it with makepkg. makepkg is Arch-specific and
# refuses to run as root, so run this as your normal user — the install step
# calls sudo pacman itself.
#
# The version comes from git: the tag on master/main, the branch name elsewhere.
#
# Usage:
#   scripts/build-arch.sh                  # build, then pacman -U the result
#   scripts/build-arch.sh --no-install     # build only (CI / packaging runs)
#   scripts/build-arch.sh --sync-deps      # let makepkg -s pacman-install makedeps
#   scripts/build-arch.sh --print-version  # show the version that would be built
#   OUTPUT_DIR=dist scripts/build-arch.sh  # also copy the .pkg.tar.zst there
set -euo pipefail

REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"

INSTALL=1
SYNC_DEPS=0
PRINT_ONLY=0
for arg in "$@"; do
  case "$arg" in
    --no-install)    INSTALL=0 ;;
    --install)       INSTALL=1 ;;
    --sync-deps)     SYNC_DEPS=1 ;;
    --print-version) PRINT_ONLY=1 ;;
    -h|--help)       sed -n '2,15p' "${BASH_SOURCE[0]}"; exit 0 ;;
    *) echo "❌ Unknown option: $arg (try --help)" >&2; exit 2 ;;
  esac
done

[[ $PRINT_ONLY -eq 1 ]] || command -v makepkg >/dev/null || {
  echo "❌ makepkg not found — this script must run on an Arch-based system." >&2
  exit 1
}
[[ $PRINT_ONLY -eq 1 || $EUID -ne 0 ]] || {
  echo "❌ Do not run this as root; makepkg refuses to build as root." >&2
  exit 1
}

# --- Version -----------------------------------------------------------------
# Release builds (master/main, or a detached checkout as CI does on a tag) take
# their version from the git tag. Branch builds take it from the branch name, so
# a package sitting in /var/cache/pacman or `pacman -Qi bikecontrol` says which
# branch it came from. Both get an .r<commits>.g<sha> suffix so two builds of the
# same branch/tag are distinguishable and sort in commit order.
#
# pacman forbids hyphens, colons, slashes and whitespace in a pkgver, so branch
# and tag names are sanitised to underscores. Note that pacman's vercmp ranks a
# leading digit above a leading letter, so installing a branch build on top of a
# release build reads as a "downgrade" — harmless, and --noconfirm accepts it.
_cargo_version() {
  sed -n '/^\[workspace\.package\]/,/^\[/ s/^version[[:space:]]*=[[:space:]]*"\([^"]*\)".*/\1/p' \
    "$REPO_ROOT/Cargo.toml" | head -1
}
_sanitize() { printf '%s' "$1" | sed 's/[^A-Za-z0-9._+]/_/g'; }

# Sets the globals PKGVER and VERSION_SOURCE (rather than echoing, so the
# "where did this version come from" note survives out of a subshell).
derive_pkgver() {
  local cargover branch desc tag rest count sha
  cargover="$(_cargo_version)"
  [[ -n "$cargover" ]] || { echo "❌ Could not read version from Cargo.toml" >&2; exit 1; }

  if ! git -C "$REPO_ROOT" rev-parse --git-dir >/dev/null 2>&1; then
    VERSION_SOURCE="Cargo.toml (not a git checkout)"
    PKGVER="$cargover"; return
  fi

  count="$(git -C "$REPO_ROOT" rev-list --count HEAD 2>/dev/null || echo 0)"
  sha="g$(git -C "$REPO_ROOT" rev-parse --short HEAD 2>/dev/null || echo unknown)"
  # Empty on a detached HEAD — that is how CI checks out a tag, so treat it as a
  # release build rather than a branch build.
  branch="$(git -C "$REPO_ROOT" symbolic-ref --quiet --short HEAD 2>/dev/null || true)"

  if [[ -z "$branch" || "$branch" == master || "$branch" == main ]]; then
    if desc="$(git -C "$REPO_ROOT" describe --tags --long 2>/dev/null)"; then
      # v0.2.0-3-gabc1234 -> tag=v0.2.0, count=3. Strips the trailing two
      # hyphen-separated fields, so a tag containing hyphens survives intact.
      tag="${desc%-*-*}"; rest="${desc#"$tag"-}"; count="${rest%-*}"; sha="${rest#*-}"
      VERSION_SOURCE="git tag ${desc%-*-*}"
      tag="$(_sanitize "${tag#v}")"
      if [[ "$count" == 0 ]]; then
        PKGVER="$tag"
      else
        VERSION_SOURCE="$VERSION_SOURCE +$count commits"
        PKGVER="$tag.r$count.$sha"
      fi
    else
      VERSION_SOURCE="Cargo.toml (no tags yet)"
      PKGVER="$cargover.r$count.$sha"
    fi
  else
    VERSION_SOURCE="branch $branch"
    PKGVER="$(_sanitize "$branch").r$count.$sha"
  fi
}

# BIKECONTROL_PKGVER may be set by the caller (CI) to pin the version exactly.
if [[ -n "${BIKECONTROL_PKGVER:-}" ]]; then
  PKGVER="$BIKECONTROL_PKGVER"
  VERSION_SOURCE="\$BIKECONTROL_PKGVER"
else
  derive_pkgver
fi

if [[ $PRINT_ONLY -eq 1 ]]; then
  echo "$PKGVER"
  echo "(from $VERSION_SOURCE)" >&2
  exit 0
fi

echo "🚴 Building BikeControl Arch package $PKGVER"
echo "   version from: $VERSION_SOURCE"
echo "============================================="

# Build straight out of the working tree; give cargo a writable, repo-local home
# so a makepkg run never fights with the user's ~/.cargo permissions.
export BIKECONTROL_SRC="$REPO_ROOT"
export BIKECONTROL_PKGVER="$PKGVER"
export CARGO_HOME="${CARGO_HOME:-$REPO_ROOT/.cargo-arch}"

if [[ $SYNC_DEPS -eq 0 ]]; then
  # Without -s, makepkg only checks that the makedeps are present. rustup users
  # have cargo on PATH without the `rust` package installed, so warn rather than
  # drag in a duplicate toolchain.
  for tool in cargo npm node pkg-config; do
    command -v "$tool" >/dev/null || echo "⚠️  $tool not on PATH — build may fail (or rerun with --sync-deps)"
  done
fi

cd "$REPO_ROOT/packaging/arch"
MAKEPKG_ARGS=(-f --noconfirm --nodeps)
[[ $SYNC_DEPS -eq 1 ]] && MAKEPKG_ARGS=(-sf --noconfirm)
makepkg "${MAKEPKG_ARGS[@]}"

PKG="$(ls -1t ./*.pkg.tar.zst | head -1)"
echo ""
echo "✅ Built $PKG"

if [[ -n "${OUTPUT_DIR:-}" ]]; then
  mkdir -p "$OUTPUT_DIR"
  cp -v "$PKG" "$OUTPUT_DIR/"
  echo "📦 Copied to $OUTPUT_DIR"
fi

if [[ $INSTALL -eq 1 ]]; then
  echo ""
  echo "📥 Installing $PKG (sudo pacman -U)"
  sudo pacman -U --noconfirm "$PKG"
  echo ""
  echo "✅ Installed. Run it with:  bikecontrol"
  echo "   Example profiles:        /usr/share/bikecontrol/profiles/"
else
  echo ""
  echo "ℹ️  Skipped install. To install manually:  sudo pacman -U $PWD/${PKG#./}"
fi
