#!/bin/sh
# Plex Discord Presence - installer for macOS and Linux.
#
#   curl -fsSL https://zeroxmrcl.github.io/plex-discord-presence-web/install.sh | sh
#   curl -fsSL https://zeroxmrcl.github.io/plex-discord-presence-web/install.sh | sh -s -- uninstall
#
# What it does, in order:
#   1. Uses the Node.js on your PATH if it is version 18 or newer. Otherwise it
#      downloads Node.js (pinned version below) into your home folder and
#      checks its SHA-256 before unpacking. Nothing is installed system-wide.
#   2. Downloads presence.js at a pinned release and checks its SHA-256.
#   3. Runs "presence.js setup" (Plex sign-in in your browser), "install"
#      (autostart at login for your user) and "status".
#
# It never uses sudo, only talks HTTPS to nodejs.org and GitHub, and sends no
# telemetry. Everything lands in ~/.plex-discord-presence (or in
# $PLEX_DISCORD_PRESENCE_HOME if you set it).
#
# Options (environment variables):
#   PDP_DOWNLOAD_ONLY=1   download and verify everything, then stop before setup
#
# The whole script is wrapped in main() and only called on the last line, so
# a download that breaks off half-way runs nothing.

set -eu

# ---------- pinned versions (update together with the checksums) ----------
APP_VERSION="2.1.0"
APP_COMMIT="84a6880ccb2a7d44fb993967ce0237b493c3a0f3"   # tag v2.1.0
APP_URL="https://raw.githubusercontent.com/zeroxmrcl/plex-discord-presence/${APP_COMMIT}/presence.js"
APP_SHA256="534e88c1a359e0a8dcf91481dee0285158798ce84bb0f4ae6dc46b4f74558dbd"

NODE_VERSION="24.22.1"
NODE_MIN_MAJOR=18
# From https://nodejs.org/dist/v24.22.1/SHASUMS256.txt
NODE_SHA256_darwin_arm64="89c682593d76cd44233fa53b1563264e7de9cbb189cad169135c5b4fadfbc0a7"
NODE_SHA256_darwin_x64="d132557e7ee2b85af08b0c8c34338e65c6430f2238d01f727728ef42dc83b035"
NODE_SHA256_linux_arm64="33c37be234ba8bee041961047b19160f4d7d0f164e8e246f78bdd10feff148b3"
NODE_SHA256_linux_x64="eb6728e77c576a35162c122c45ccadf224d89ff00abc8dae46cc2897c6002d7c"

# ---------- helpers ----------
say()  { printf '%s\n' "==> $*"; }
warn() { printf '%s\n' "warning: $*" >&2; }
die()  { printf '%s\n' "error: $*" >&2; exit 1; }

# Download $1 to $2 over HTTPS only (TLS 1.2+), with curl or wget.
download() {
  case "$1" in https://*) ;; *) die "refusing non-HTTPS URL: $1" ;; esac
  if command -v curl >/dev/null 2>&1; then
    curl --proto '=https' --tlsv1.2 --fail --silent --show-error --location \
      --retry 3 --output "$2" "$1"
  elif command -v wget >/dev/null 2>&1; then
    wget --https-only --secure-protocol=TLSv1_2 --quiet --tries=3 --output-document="$2" "$1"
  else
    die "curl or wget is required"
  fi
}

# Print the SHA-256 of file $1.
sha256_of() {
  if command -v sha256sum >/dev/null 2>&1; then
    sha256sum "$1" | cut -d ' ' -f 1
  elif command -v shasum >/dev/null 2>&1; then
    shasum -a 256 "$1" | cut -d ' ' -f 1
  else
    die "sha256sum or shasum is required to verify downloads"
  fi
}

# Fail unless file $1 has SHA-256 $2.
verify() {
  actual=$(sha256_of "$1")
  if [ "$actual" != "$2" ]; then
    rm -f "$1"
    die "checksum mismatch for $(basename "$1")
  expected $2
  got      $actual
The download was deleted. Please try again later or report this."
  fi
}

# Major version of the node binary $1, or empty if it does not run.
node_major() {
  "$1" -p 'process.versions.node.split(".")[0]' 2>/dev/null || true
}

# Node.js platform name for this machine, e.g. linux-x64 or darwin-arm64.
detect_platform() {
  os=$(uname -s)
  arch=$(uname -m)
  case "$os" in
    Darwin) os=darwin ;;
    Linux)  os=linux ;;
    *) die "unsupported system: $os (this installer is for macOS and Linux; on Windows use install.ps1)" ;;
  esac
  case "$arch" in
    x86_64|amd64)  arch=x64 ;;
    arm64|aarch64) arch=arm64 ;;
    *) die "unsupported CPU: $arch. Install Node.js $NODE_MIN_MAJOR+ yourself (https://nodejs.org) and run this again." ;;
  esac
  # A Terminal running under Rosetta reports x86_64 on Apple Silicon.
  if [ "$os" = darwin ] && [ "$arch" = x64 ] &&
     [ "$(sysctl -n sysctl.proc_translated 2>/dev/null || echo 0)" = 1 ]; then
    arch=arm64
  fi
  # The official Linux builds need glibc; Alpine and friends use musl.
  if [ "$os" = linux ] && ldd --version 2>&1 | grep -qi musl; then
    die "this system uses musl libc, which the official Node.js builds do not support.
Install Node.js $NODE_MIN_MAJOR+ with your package manager (e.g. 'apk add nodejs') and run this again."
  fi
  printf '%s-%s' "$os" "$arch"
}

# Make sure a suitable node is available and on PATH. Sets $NODE.
ensure_node() {
  local_bin="$PDP_HOME/node/bin"

  # 1. A node on PATH that is new enough.
  if command -v node >/dev/null 2>&1; then
    major=$(node_major node)
    if [ -n "$major" ] && [ "$major" -ge "$NODE_MIN_MAJOR" ]; then
      NODE=$(command -v node)
      say "Using Node.js $("$NODE" --version) from $NODE"
      return
    fi
    warn "Node.js on PATH is too old ($(node --version 2>/dev/null || echo unknown)); a private copy will be used."
  fi

  # 2. Our own copy from an earlier run.
  if [ -x "$local_bin/node" ] && [ "$("$local_bin/node" --version 2>/dev/null)" = "v$NODE_VERSION" ]; then
    NODE="$local_bin/node"
    PATH="$local_bin:$PATH"; export PATH
    say "Using Node.js v$NODE_VERSION from $local_bin"
    return
  fi

  # 3. Download the pinned version into ~/.plex-discord-presence/node.
  platform=$(detect_platform)
  name="node-v$NODE_VERSION-$platform"
  eval "expected=\$NODE_SHA256_$(printf '%s' "$platform" | tr '-' '_')"
  say "Downloading Node.js v$NODE_VERSION ($platform) from nodejs.org"
  download "https://nodejs.org/dist/v$NODE_VERSION/$name.tar.gz" "$TMP/$name.tar.gz"
  verify "$TMP/$name.tar.gz" "$expected"
  say "Checksum OK, unpacking to $PDP_HOME/node"
  tar -xzf "$TMP/$name.tar.gz" -C "$TMP"
  # Swap in the new copy only after it unpacked completely.
  rm -rf "$PDP_HOME/node"
  mkdir -p "$PDP_HOME"
  mv "$TMP/$name" "$PDP_HOME/node"

  NODE="$local_bin/node"
  # Put our node first on PATH: "presence.js install" picks the node it finds
  # there for the autostart entry, and this path stays stable across updates.
  PATH="$local_bin:$PATH"; export PATH
  say "Installed Node.js $("$NODE" --version)"
}

# Run presence.js with the terminal as input, even when this script itself
# is read from a pipe (curl ... | sh), so the setup prompts work.
run_app() {
  if (exec </dev/tty) 2>/dev/null; then
    "$NODE" "$APP" "$@" </dev/tty
  else
    "$NODE" "$APP" "$@"
  fi
}

do_uninstall() {
  installed="$PDP_HOME/app/presence.js"
  if [ ! -f "$installed" ]; then
    say "Nothing to uninstall in $PDP_HOME/app"
  else
    NODE=$(command -v node 2>/dev/null || true)
    [ -x "$PDP_HOME/node/bin/node" ] && NODE="$PDP_HOME/node/bin/node"
    [ -n "$NODE" ] || die "no Node.js found to run the uninstaller"
    APP="$installed"
    run_app uninstall
  fi
  if [ -d "$PDP_HOME/node" ]; then
    rm -rf "$PDP_HOME/node"
    say "Removed the private Node.js copy"
  fi
  say "Done. Your config and Plex sign-in are kept in $PDP_HOME."
  say "To delete them as well: rm -rf \"$PDP_HOME\""
}

main() {
  action="${1:-install}"
  PDP_HOME="${PLEX_DISCORD_PRESENCE_HOME:-$HOME/.plex-discord-presence}"

  # No sudo: autostart is per user, and root would put files in the wrong home.
  if [ "$(id -u)" = 0 ]; then
    die "please run this as your normal user, not as root or with sudo"
  fi

  case "$action" in
    install) ;;
    uninstall) do_uninstall; return ;;
    *) die "unknown action '$action' (use install or uninstall)" ;;
  esac

  say "Installing Plex Discord Presence $APP_VERSION"
  TMP=$(mktemp -d 2>/dev/null || mktemp -d -t pdp)
  trap 'rm -rf "$TMP"' EXIT
  trap 'exit 130' INT TERM

  ensure_node

  say "Downloading presence.js $APP_VERSION from GitHub"
  APP="$TMP/presence.js"
  download "$APP_URL" "$APP"
  verify "$APP" "$APP_SHA256"
  say "Checksum OK"

  if [ "${PDP_DOWNLOAD_ONLY:-}" = 1 ]; then
    say "PDP_DOWNLOAD_ONLY=1: stopping before setup. Verified app: $APP"
    say "presence.js $("$NODE" "$APP" --version) runs with Node.js $("$NODE" --version)"
    return
  fi

  say "Step 1/3: setup (a browser window opens for the Plex sign-in)"
  run_app setup
  say "Step 2/3: install (start automatically at login)"
  run_app install
  say "Step 3/3: status"
  run_app status

  echo
  say "Done. Plex Discord Presence now starts at login."
  say "Uninstall later with: curl -fsSL https://zeroxmrcl.github.io/plex-discord-presence-web/install.sh | sh -s -- uninstall"
}

main "$@"
