#!/usr/bin/env bash
set -euo pipefail

# Satis / Forge server IP — update before releasing
SATIS_SERVER_IP="159.65.202.73"

current_version() {
  node -p "require('./package.json').version"
}

next_version() {
  local version="$1"
  local bump="$2"
  local major minor patch
  IFS='.' read -r major minor patch <<<"$version"

  if [[ -z "$major" || -z "$minor" || -z "$patch" ]]; then
    echo "Invalid semantic version '$version' (expected X.Y.Z)." >&2
    exit 1
  fi

  case "$bump" in
    patch)
      patch=$((patch + 1))
      ;;
    minor)
      minor=$((minor + 1))
      patch=0
      ;;
    major)
      major=$((major + 1))
      minor=0
      patch=0
      ;;
    *)
      echo "Unknown bump type '$bump' (use patch|minor|major)." >&2
      exit 1
      ;;
  esac

  echo "${major}.${minor}.${patch}"
}

resolve_version() {
  local requested="${1:-}"

  if [[ -n "$requested" ]]; then
    case "$requested" in
      patch|minor|major)
        local base
        base="$(current_version)"
        next_version "$base" "$requested"
        return
        ;;
      *)
        echo "$requested"
        return
        ;;
    esac
  fi

  if [[ ! -t 0 ]]; then
    echo "No version provided and no interactive terminal available." >&2
    echo "Usage: ./release.sh [--full-satis] <version|patch|minor|major>" >&2
    exit 1
  fi

  local base bump
  base="$(current_version)"
  echo "Current version: ${base}" >&2
  read -r -p "Release type (patch/minor/major): " bump
  bump="${bump//[$'\t\r\n ']}"

  case "$bump" in
    patch|minor|major)
      next_version "$base" "$bump"
      ;;
    *)
      echo "Invalid release type '$bump'. Use patch, minor, or major." >&2
      exit 1
      ;;
  esac
}

FULL_SATIS_REBUILD=0
POSITIONAL=()
for arg in "$@"; do
  case "$arg" in
    --full-satis) FULL_SATIS_REBUILD=1 ;;
    *)
      POSITIONAL+=("$arg")
      ;;
  esac
done

if [[ ${#POSITIONAL[@]} -gt 1 ]]; then
  echo "Usage: ./release.sh [--full-satis] [version|patch|minor|major]" >&2
  echo "       ./release.sh [version|patch|minor|major] [--full-satis]" >&2
  echo "Examples: ./release.sh patch   |   ./release.sh 1.0.9 --full-satis" >&2
  echo "          npm run release      |   npm run release -- minor" >&2
  echo "          --full-satis: wipe Composer VCS cache + Satis public/, full mirror rebuild" >&2
  exit 1
fi

VERSION="$(resolve_version "${POSITIONAL[0]:-}")"

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"

# Commit any pending changes first
if ! git diff --quiet || ! git diff --cached --quiet || \
   [[ -n "$(git ls-files --others --exclude-standard)" ]]; then
  git add -A
  git commit -m "chore: pre-release changes for v${VERSION}"
fi

export RELEASE_VERSION="$VERSION"
node <<'EOS'
const fs = require('fs');

const ver = process.env.RELEASE_VERSION;
for (const [file, indent] of [
  ['package.json', 2],
  ['composer.json', 4],
]) {
  const o = JSON.parse(fs.readFileSync(file, 'utf8'));
  o.version = ver;
  fs.writeFileSync(file, JSON.stringify(o, null, indent) + '\n');
}
EOS

# Stage only version files (avoids `git add .` scanning the whole tree — on some
# setups that can SIGBUS if the index is corrupt or a path triggers a git bug).
git add package.json composer.json
git diff --cached --quiet && {
  echo "No version change to commit (files already at v${VERSION})?" >&2
  exit 1
}
git commit -m "chore: release v${VERSION}"
git tag "v${VERSION}"
git push origin main
git push origin "v${VERSION}"
npm publish --registry http://maestro-npm.build2connect.nl --access public

if [[ "$FULL_SATIS_REBUILD" -eq 1 ]]; then
  echo "Running full Satis rebuild (Composer VCS cache + public/ wiped)…" >&2
  ssh "forge@${SATIS_SERVER_IP}" "cd /home/forge/satis && rm -rf /home/forge/.cache/composer/vcs/* public/* && php bin/satis build satis.json public/"
else
  ssh "forge@${SATIS_SERVER_IP}" "cd /home/forge/satis && php bin/satis build satis.json public/"
fi

echo "Release v${VERSION} complete!"
