#!/bin/sh
# Install the latest signed Offsider release. Requires macOS 26 or later.
#   curl -fsSL https://offsider.website/install.sh | sh
# Optional: OFFSIDER_INSTALL_DIR (default ~/Applications),
#           OFFSIDER_BIN_DIR (default ~/.local/bin).
set -eu

fail() { printf '\noffsider install: %s\n' "$1" >&2; exit 1; }
step() { printf '  %s\n' "$1" >&2; }

main() {
  [ "$(uname -s)" = Darwin ] || fail 'Offsider requires macOS 26 or later.'
  major=$(sw_vers -productVersion | cut -d . -f 1)
  case "$major" in ''|*[!0-9]*) fail 'Could not determine the macOS version.' ;; esac
  [ "$major" -ge 26 ] || fail 'Offsider requires macOS 26 or later.'
  case "$(uname -m)" in
    arm64|x86_64) ;;
    *) fail 'Unsupported Mac architecture.' ;;
  esac

  app_dir=${OFFSIDER_INSTALL_DIR:-$HOME/Applications}
  bin_dir=${OFFSIDER_BIN_DIR:-$HOME/.local/bin}
  case "$app_dir" in /*) ;; *) fail 'OFFSIDER_INSTALL_DIR must be an absolute path.' ;; esac
  case "$bin_dir" in /*) ;; *) fail 'OFFSIDER_BIN_DIR must be an absolute path.' ;; esac
  mkdir -p "$app_dir" "$bin_dir"
  app_dir=$(cd "$app_dir" && pwd -P)
  bin_dir=$(cd "$bin_dir" && pwd -P)
  installed="$app_dir/Offsider.app"
  link="$bin_dir/offsider"
  helper="$installed/Contents/Helpers/offsider"
  case "$bin_dir/" in "$installed/"*) fail 'The command directory must be outside Offsider.app.' ;; esac

  [ ! -L "$installed" ] || fail "Refusing to replace an app symlink: $installed"
  if [ -e "$installed" ]; then
    identity=$(/usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' "$installed/Contents/Info.plist" 2>/dev/null || true)
    [ "$identity" = com.unreasonable-magic.offsider ] || fail "Refusing to replace an unrelated file: $installed"
  fi
  if [ -L "$link" ]; then
    [ "$(readlink "$link")" = "$helper" ] || fail "An unrelated symlink already exists at $link"
  elif [ -e "$link" ]; then
    fail "Refusing to replace an unrelated file: $link"
  fi

  stage=$(mktemp -d "$app_dir/.offsider-install.XXXXXX")
  complete=false
  new_installed=false
  new_link=false
  cleanup() {
    if [ "$complete" != true ]; then
      if [ "$new_link" = true ]; then rm -f "$link"; fi
      if [ "$new_installed" = true ]; then rm -rf "$installed"; fi
      if [ -e "$stage/previous.app" ]; then
        mv "$stage/previous.app" "$installed" || { step "Previous app preserved in $stage"; return; }
      fi
      if [ -L "$stage/previous-command" ]; then
        mv "$stage/previous-command" "$link" || { step "Previous command preserved in $stage"; return; }
      fi
    fi
    rm -rf "$stage"
  }
  trap cleanup EXIT
  trap 'exit 130' INT
  trap 'exit 143' TERM

  step 'Downloading the latest Offsider release…'
  curl --fail --location --show-error --silent --proto '=https' --proto-redir '=https' \
    --connect-timeout 15 --max-time 600 --retry 2 \
    https://offsider.website/download -o "$stage/release.zip" || fail 'Download failed; please try again.'
  step 'Extracting and verifying Offsider…'
  ditto -x -k "$stage/release.zip" "$stage/unpacked" || fail 'Could not extract the release archive.'
  app="$stage/unpacked/Offsider.app"
  [ -d "$app" ] && [ ! -L "$app" ] || fail 'The archive does not contain Offsider.app.'
  codesign --verify --deep --strict \
    -R='anchor apple generic and identifier "com.unreasonable-magic.offsider" and certificate leaf[subject.OU] = "M5XJQGR7S2"' \
    "$app" || fail 'The downloaded app failed signature verification.'
  spctl --assess --type execute "$app" || fail 'The downloaded app was rejected by Gatekeeper.'
  [ -x "$app/Contents/Helpers/offsider" ] || fail 'The archive is missing the offsider command.'
  version=$("$app/Contents/Helpers/offsider" --version) || fail 'This release cannot run on this Mac.'

  # Do not close panels or interrupt jobs during a terminal installation.
  running_status=0
  pgrep -u "$(id -u)" -x Offsider >/dev/null || running_status=$?
  case "$running_status" in
    0) fail 'Quit Offsider, then run the installer again. Its open panels and jobs have been left running.' ;;
    1) ;;
    *) fail 'Could not check for a running Offsider app; nothing has been replaced.' ;;
  esac

  step 'Installing the app and command…'
  if [ -e "$installed" ]; then mv "$installed" "$stage/previous.app"; fi
  if [ -L "$link" ]; then mv "$link" "$stage/previous-command"; fi
  mv "$app" "$installed"
  new_installed=true
  ln -s "$helper" "$link"
  new_link=true
  "$link" --version >/dev/null || fail 'The installed command could not run.'
  complete=true

  printf '\nInstalled %s\n  App: %s\n  Command: %s\n' "$version" "$installed" "$link"
  case ":$PATH:" in
    *":$bin_dir:"*) printf '\nRun offsider --help to get started.\n' ;;
    *) printf '\nAdd %s to your PATH, then run offsider --help.\n' "$bin_dir" ;;
  esac
}

# Keep execution last so a truncated download cannot perform a partial install.
main "$@"
