#!/usr/bin/env bash
#
# Install Pixelix Ops on a Mac.
#
#   curl -fsSL https://ops.pixelixmarketing.com/updates/install-mac.sh | bash
#
# Why this exists rather than "download the DMG and drag it across":
#
# The app is not signed with an Apple Developer certificate, so macOS cannot
# check who published it and refuses to open it — "Apple could not verify this
# app is free of malware". That warning is about the absence of a $99/year
# certificate, not about anything found in the file.
#
# A browser tags every download with com.apple.quarantine, which is what
# triggers that check. curl does not, so a file fetched this way installs
# without the argument. Buying the certificate is the real fix; until then
# this is the reliable one.
set -euo pipefail

FEED="${FEED:-https://ops.pixelixmarketing.com/updates}"
APP="Pixelix Ops.app"

# The office DNS resolver returns SERVFAIL intermittently — seen repeatedly
# while building this. --retry-all-errors is what makes curl retry a name
# resolution failure rather than giving up on the first one.
RETRY=(--retry 5 --retry-delay 3 --retry-all-errors --connect-timeout 20)

say() { printf '\033[1m==> %s\033[0m\n' "$1"; }
die() { printf '\033[31merror: %s\033[0m\n' "$1" >&2; exit 1; }

[[ "$(uname -s)" == "Darwin" ]] || die "This installer is for macOS."

# Apple silicon and Intel take different builds; picking the wrong one gives a
# copy that either will not launch or runs under translation.
case "$(uname -m)" in
  arm64)  SUFFIX="-arm64" ;;
  x86_64) SUFFIX=""       ;;
  *) die "Unsupported processor: $(uname -m)" ;;
esac

say "Finding the current version"
VERSION="$(curl -fsSL "${RETRY[@]}" "$FEED/latest-mac.yml" | sed -n 's/^version: *//p' | tr -d '\r')"
[[ -n "$VERSION" ]] || die "Could not read the version from $FEED/latest-mac.yml"
DMG="Pixelix Ops-${VERSION}${SUFFIX}.dmg"
echo "    ${VERSION} ($(uname -m))"

WORK="$(mktemp -d)"
MOUNT=""
cleanup() {
  [[ -n "$MOUNT" ]] && hdiutil detach "$MOUNT" -quiet 2>/dev/null || true
  rm -rf "$WORK"
}
trap cleanup EXIT

say "Downloading"
curl -fL --progress-bar "${RETRY[@]}" "$FEED/$(printf '%s' "$DMG" | sed 's/ /%20/g')" -o "$WORK/app.dmg" \
  || die "Could not download $DMG"

# The feed publishes a SHA-512 per file. Checking it means a download that was
# cut short or tampered with in transit fails here rather than on launch.
say "Checking the download"
EXPECTED="$(curl -fsSL "${RETRY[@]}" "$FEED/latest-mac.yml" \
  | tr -d '\r' | grep -A2 -F "url: $DMG" | sed -n 's/^ *sha512: *//p' | head -1)"
if [[ -n "$EXPECTED" ]]; then
  ACTUAL="$(shasum -a 512 "$WORK/app.dmg" | cut -d' ' -f1 | xxd -r -p | base64)"
  [[ "$ACTUAL" == "$EXPECTED" ]] || die "The download does not match the published checksum. Try again."
  echo "    checksum matches"
else
  echo "    no checksum published for this build — skipping"
fi

say "Installing"
# The app may be running from an earlier launch; replacing it underneath is
# what produces a half-updated copy that crashes on next start.
if pgrep -f "/$APP/Contents/MacOS/" >/dev/null 2>&1; then
  osascript -e 'quit app "Pixelix Ops"' 2>/dev/null || true
  sleep 2
fi

# /Applications belongs to admins. On a Mac where the person signed in is a
# standard user the copy fails with "Permission denied" — which is exactly
# what makes one machine install happily and the next refuse. macOS supports
# a per-user Applications folder for this, and Launchpad and Spotlight index
# it the same way, so fall back rather than demand a password.
DEST="/Applications"
if [ ! -w "$DEST" ]; then
  DEST="$HOME/Applications"
  mkdir -p "$DEST" || die "Cannot write to /Applications or $DEST"
  echo "    not an administrator — installing to $DEST instead"
fi

# A stale copy in /Applications would keep opening instead of the new one.
if [ "$DEST" != "/Applications" ] && [ -d "/Applications/$APP" ]; then
  echo "    note: an older copy remains in /Applications and cannot be replaced"
  echo "          without an administrator. Open this one from $DEST."
fi

# Not -quiet: that suppresses the very listing the mount point is read from,
# and the empty parse then fails the pipeline with nothing to show for it.
ATTACHED="$(hdiutil attach "$WORK/app.dmg" -nobrowse -noautoopen 2>&1)" \
  || die "Could not open the disk image:
$ATTACHED"
MOUNT="$(printf '%s\n' "$ATTACHED" | grep -o '/Volumes/.*' | head -1)"
[[ -n "$MOUNT" ]] || die "Could not find where the disk image mounted:
$ATTACHED"
[[ -d "$MOUNT/$APP" ]] || die "The disk image did not contain $APP"

rm -rf "${DEST:?}/${APP:?}"
cp -R "$MOUNT/$APP" "$DEST/" || die "Could not copy the app into $DEST"

# Belt and braces. curl leaves no quarantine flag, but a copy that picked one
# up anywhere along the way would still be blocked, and this is the whole
# point of the exercise.
xattr -dr com.apple.quarantine "$DEST/$APP" 2>/dev/null || true

say "Installed Pixelix Ops ${VERSION}"
echo "    Open it from ${DEST/#$HOME/~}."
echo "    Server address: https://ops.pixelixmarketing.com"
