#!/usr/bin/env bash
# Unvibe private beta installer for macOS. Source: https://github.com/ShadowEsu/Unvibe/blob/main/marketing/src/lib/betaInstallScript.ts
# Downloads the app from GitHub releases with curl, stops unless it matches the published
# SHA-256, and copies Unvibe.app into /Applications.
# Sends one anonymous 'installed' count to unvibe.site when it finishes. No code or personal data.
set -euo pipefail

TAG="${UNVIBE_BETA_TAG:-v0.1.44}"
VERSION="${TAG#v}"
ASSET="${UNVIBE_BETA_ASSET:-Unvibe-0.1.44-mac-arm64.dmg}"
URL="https://github.com/ShadowEsu/Unvibe/releases/download/${TAG}/${ASSET}"
DEST="/Applications/Unvibe.app"
TRACK="https://unvibe.site/api/install/event"
HELP="https://unvibe.site/install-help"

if [ "$(uname -s)" != "Darwin" ]; then
  echo "On Windows run: irm https://unvibe.site/install.ps1 | iex"
  echo "This installer is for macOS."
  exit 1
fi
if [ "$(uname -m)" != "arm64" ] && [ -z "${UNVIBE_BETA_ASSET:-}" ]; then
  # Intel Mac: use the x64 build.
  ASSET="${ASSET/mac-arm64/mac-x64}"
  URL="https://github.com/ShadowEsu/Unvibe/releases/download/${TAG}/${ASSET}"
fi

work="$(mktemp -d /tmp/unvibe-beta-XXXX)"
mountPoint="$work/volume"
device=""
cleanup() {
  if [ -n "$device" ]; then
    hdiutil detach "$device" -force >/dev/null 2>&1 || true
  fi
  rm -rf "$work"
}
trap cleanup EXIT
stop() {
  echo "$1" >&2
  echo "For help, see $HELP" >&2
  exit 1
}

echo "Downloading Unvibe beta…"
curl -fL --progress-bar "$URL" -o "$work/Unvibe.dmg"

# Check the download against the SHA-256 published with the release. No checksum, no install.
SUMS_URL="https://github.com/ShadowEsu/Unvibe/releases/download/${TAG}/SHA256SUMS-mac.txt"
if ! curl -fsSL -m 20 "$SUMS_URL" -o "$work/SHA256SUMS"; then
  stop "Could not download the published checksums from $SUMS_URL, so the download can't be checked. Nothing was installed."
fi
expected="$(awk -v a="$ASSET" '$2 == a || $2 == "*"a { print $1; exit }' "$work/SHA256SUMS")"
if [ -z "$expected" ]; then
  stop "The published checksums have no line for $ASSET, so the download can't be checked. Nothing was installed."
fi
actual="$(shasum -a 256 "$work/Unvibe.dmg" | awk '{ print $1 }')"
if [ "$expected" != "$actual" ]; then
  stop "The download does not match the published checksum. Nothing was installed."
fi
echo "Checksum verified."

mkdir -p "$mountPoint"
attachOut="$(hdiutil attach -nobrowse -readonly -mountpoint "$mountPoint" "$work/Unvibe.dmg")"
device="$(printf '%s\n' "$attachOut" | awk '/^\/dev\/disk/ { print $1; exit }')"
# A symlink named Unvibe.app passes -d and the signature check (codesign checks its target, which could be
# the installed copy), and the rm -rf below would then delete the real install. Only a real folder goes on.
if [ ! -d "$mountPoint/Unvibe.app" ] || [ -L "$mountPoint/Unvibe.app" ]; then
  stop "The disk image did not contain Unvibe.app as a real folder. Nothing was installed."
fi
# The release is self-signed ("Unvibe Self Signed", not an Apple Developer ID), and only an app signed
# with that certificate passes: a copy re-signed by anyone else fails. If the check fails, stop rather
# than re-sign it here: a local re-sign changes the app's identity and macOS drops its Accessibility permission.
if ! codesign --verify --deep --strict -R='identifier "com.unvibe.app" and certificate leaf = H"c9da4f618adf98911a450e041b8f336574a99d0e"' "$mountPoint/Unvibe.app" >/dev/null 2>&1; then
  # Who signed the copy that failed, so support can tell a certificate change from tampering. The names
  # come from that copy's own certificate, so only printable characters go to the terminal.
  echo "Signature on the downloaded app (for support):" >&2
  codesign -dvv "$mountPoint/Unvibe.app" 2>&1 | grep '^Authority=' | LC_ALL=C tr -cd '[:print:]\n' | head -n 3 >&2 || true
  stop "The downloaded app failed its signature check. Nothing was installed, and any Unvibe you already have is unchanged."
fi

# The pin says who signed the app, not which release it is. Only the version this script installs goes on,
# so an older signed build can't be installed in its place.
appVersion="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' "$mountPoint/Unvibe.app/Contents/Info.plist" 2>/dev/null || true)"
if [ "$appVersion" != "$VERSION" ]; then
  stop "The downloaded app is not version $VERSION. Nothing was installed, and any Unvibe you already have is unchanged."
fi

# Copy next to the installed app first and swap only once the copy is complete, so a failed copy (a full
# disk, say) leaves any installed Unvibe as it was. A leftover copy from an interrupted run goes first.
echo "Copying Unvibe…"
rm -rf "$DEST.new"
if ! ditto "$mountPoint/Unvibe.app" "$DEST.new"; then
  rm -rf "$DEST.new"
  stop "Could not copy Unvibe into Applications. Nothing was installed, and any Unvibe you already have is unchanged."
fi

echo "Closing any running Unvibe…"
osascript -e 'tell application id "com.unvibe.app" to quit' >/dev/null 2>&1 || true
sleep 1
pkill -x Unvibe >/dev/null 2>&1 || true

echo "Installing to Applications…"
# Move the installed app aside rather than deleting it first: a delete that fails halfway (files another
# admin's install left owned by root, say) would leave half an app and no new one. A leftover .old that
# can't be deleted is renamed, so the installed app is never moved inside it.
rm -rf "$DEST.old" 2>/dev/null || true
if [ -e "$DEST.old" ] || [ -L "$DEST.old" ]; then
  mv "$DEST.old" "$DEST.old.$$" 2>/dev/null || true
fi
if [ -e "$DEST.old" ] || [ -L "$DEST.old" ]; then
  rm -rf "$DEST.new" 2>/dev/null || true
  stop "Could not clear $DEST.old. Nothing was installed, and any Unvibe you already have is unchanged."
fi
if [ -e "$DEST" ] || [ -L "$DEST" ]; then
  if ! mv "$DEST" "$DEST.old"; then
    rm -rf "$DEST.new" 2>/dev/null || true
    stop "Could not move the installed Unvibe aside. Nothing was installed, and any Unvibe you already have is unchanged."
  fi
fi
if ! mv "$DEST.new" "$DEST"; then
  if [ -e "$DEST.old" ] || [ -L "$DEST.old" ]; then
    if ! mv "$DEST.old" "$DEST"; then
      rm -rf "$DEST.new" 2>/dev/null || true
      stop "Could not move the new Unvibe into place or put the old one back. Your previous Unvibe is at $DEST.old."
    fi
  fi
  rm -rf "$DEST.new" 2>/dev/null || true
  stop "Could not move the new Unvibe into place. Nothing was installed, and any Unvibe you already have is unchanged."
fi
# The old copy goes last. If it can't be deleted, the new app is already in place, and the next run renames it.
rm -rf "$DEST.old" 2>/dev/null || true

echo "Opening Unvibe…"
open "$DEST"
curl -fsS -m 4 -X POST "$TRACK" -H "Content-Type: application/json" -d '{"event":"installed"}' >/dev/null 2>&1 || true
echo "Done. If macOS blocks it, see https://unvibe.site/install-help"
