#!/bin/sh
# InstaOutreach for Mac: install.
#   curl -fsSL https://instaoutreach.com/desktop/install.sh | sh
# Once installed, the app updates itself (signed updates), so running this again only opens it.
# To replace a damaged install:  curl -fsSL https://instaoutreach.com/desktop/install.sh | sh -s -- --reinstall
# A reinstall never goes back to an older version than the one installed.
# Everything runs inside main(), called on the last line: a download cut off half-way
# defines a function and stops, instead of running the half that arrived.
main() {
set -eu

BASE="https://instaoutreach.com"
APP="InstaOutreach"
REINSTALL=0
[ "${1:-}" = "--reinstall" ] && REINSTALL=1

say() { printf '%s\n' "$*"; }
fail() { printf '\nInstaOutreach was not installed: %s\n' "$*" >&2; exit 1; }
# True when dotted version $1 is older than $2.
older() {
  [ "$1" != "$2" ] && [ "$(printf '%s\n%s\n' "$1" "$2" | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n | head -n 1)" = "$1" ]
}

[ "$(uname -s)" = "Darwin" ] || fail "this installer is for macOS only."

INSTALLED=""
for d in /Applications "$HOME/Applications"; do
  if [ -d "$d/$APP.app" ]; then INSTALLED="$d/$APP.app"; break; fi
done
if [ -n "$INSTALLED" ] && [ "$REINSTALL" -eq 0 ]; then
  say "InstaOutreach is already installed in ${INSTALLED%/*}."
  say "It updates itself: when a new version is out, the app downloads it, checks its signature and asks to restart."
  say "Opening InstaOutreach..."
  open "$INSTALLED" || say "It did not open by itself. Open InstaOutreach from ${INSTALLED%/*}."
  exit 0
fi

case "$(uname -m)" in
  arm64) ARCH=arm64 ;;
  x86_64) ARCH=x64 ;;
  *) fail "unsupported processor type $(uname -m)." ;;
esac
# An Apple silicon Mac running this shell under Rosetta still wants the native build.
if [ "$ARCH" = "x64" ] && [ "$(sysctl -n sysctl.proc_translated 2>/dev/null || echo 0)" = "1" ]; then
  ARCH=arm64
fi
if [ "$ARCH" = "arm64" ]; then CHIP="Apple silicon"; else CHIP="Intel"; fi

# /Applications is writable for admin users with no password prompt. A standard (non-admin) user gets
# the app in ~/Applications instead of a password prompt.
if [ -n "$INSTALLED" ]; then DIR="${INSTALLED%/*}"
elif [ -w /Applications ]; then DIR="/Applications"; else DIR="$HOME/Applications"; mkdir -p "$DIR"; fi
DEST="$DIR/$APP.app"
STAGE="$DIR/.$APP-update.$$"

TMP="$(mktemp -d "${TMPDIR:-/tmp}/instaoutreach.XXXXXX")" || fail "could not create a temporary folder."
cleanup() { rm -rf "$TMP" "$STAGE"; }
trap cleanup EXIT
trap 'exit 130' INT TERM

say "Downloading InstaOutreach for $CHIP Macs..."
curl -fL --progress-bar -D "$TMP/headers" -o "$TMP/app.zip" "$BASE/desktop/download?arch=$ARCH" \
  || fail "the download did not finish. Check your internet connection and run the command again."

# Ask for the checksum of exactly the version just downloaded, even if a newer one was
# published a second ago.
VER="$(tr -d '\r' < "$TMP/headers" | tr 'A-Z' 'a-z' | sed -n 's/^x-desktop-version:[[:space:]]*//p' | tail -n 1)"
case "$VER" in ""|*[!0-9.]*) VER="" ;; esac
QUERY="arch=$ARCH"
[ -n "$VER" ] && QUERY="$QUERY&v=$VER"

say "Checking the download..."
EXPECTED="$(curl -fsSL "$BASE/desktop/download.sha256?$QUERY" | tr -d ' \r\n' | tr 'A-F' 'a-f')" || EXPECTED=""
case "$EXPECTED" in ""|*[!0-9a-f]*) fail "could not fetch the checksum to verify the download. Run the command again." ;; esac
[ "${#EXPECTED}" -eq 64 ] || fail "could not fetch the checksum to verify the download. Run the command again."
ACTUAL="$(shasum -a 256 "$TMP/app.zip" | awk '{print $1}')"
if [ "$ACTUAL" != "$EXPECTED" ]; then
  fail "the downloaded file does not match its checksum, so it was damaged or changed on the way. Nothing on your Mac was changed. Run the command again."
fi
say "Download verified."

if [ -n "$INSTALLED" ]; then
  HAVE="$(defaults read "$INSTALLED/Contents/Info" CFBundleShortVersionString 2>/dev/null || true)"
  case "$HAVE" in ""|*[!0-9.]*) HAVE="" ;; esac
  [ -n "$VER" ] || fail "could not tell which version was downloaded, so the installed one was left alone. Run the command again."
  if [ -n "$HAVE" ] && older "$VER" "$HAVE"; then
    fail "the download is version $VER, older than the installed $HAVE. Nothing on your Mac was changed."
  fi
fi

# The main process runs as Contents/MacOS/Electron, so match the bundle path, not a name.
if pgrep -f "$APP.app/Contents/MacOS/" >/dev/null 2>&1; then
  say "Closing the running InstaOutreach..."
  osascript -e 'tell application "InstaOutreach" to quit' >/dev/null 2>&1 || true
  i=0
  while pgrep -f "$APP.app/Contents/MacOS/" >/dev/null 2>&1; do
    i=$((i + 1))
    [ "$i" -ge 20 ] && fail "InstaOutreach did not close within 20 seconds. Quit it yourself (Cmd+Q) and run the command again."
    sleep 1
  done
fi

say "Unpacking..."
mkdir "$TMP/x"
ditto -x -k "$TMP/app.zip" "$TMP/x" || fail "the download could not be unpacked."
SRC="$(find "$TMP/x" -maxdepth 2 -type d -name "$APP.app" | head -n 1)"
[ -n "$SRC" ] || fail "the download does not contain $APP.app."

say "Installing into $DIR..."
rm -rf "$STAGE"
ditto "$SRC" "$STAGE" || fail "could not copy the app into $DIR."
if [ -e "$DEST" ]; then
  rm -rf "$DEST" || fail "could not replace the old $DEST. Move it to the Bin yourself and run the command again."
fi
mv "$STAGE" "$DEST" || fail "could not move the app into $DIR."
xattr -dr com.apple.quarantine "$DEST" 2>/dev/null || true

say "Opening InstaOutreach..."
open "$DEST" || say "Installed, but it did not open by itself. Open InstaOutreach from $DIR."
if [ -n "$VER" ]; then say "Done. InstaOutreach $VER is installed in $DIR."; else say "Done. InstaOutreach is installed in $DIR."; fi
say "From now on InstaOutreach updates itself. There is no need to run this command again."
}

main "$@"
