#!/bin/sh
# VIA Quick setup — public, verified private Node runtime and existing VIA setup.
set -eu
umask 077

NODE_VERSION="v24.21.0"
BASE_URL="https://nodejs.org/dist/${NODE_VERSION}"
VIA_PACKAGE="@thevia/via@latest"

usage() {
  printf '%s\n' "VIA Quick setup" \
    "Usage: sh quick-setup.sh [--verify-only|--macos-preflight|--help]" \
    "Default: prepare private Node.js and run the existing VIA setup." \
    "--verify-only: check runtime provisioning without running VIA setup." \
    "--macos-preflight: read-only macOS user-session and Keychain check; does not install."
}

case "${1:-}" in
  --help|-h) usage; exit 0 ;;
  --verify-only) VERIFY_ONLY=1; MACOS_PREFLIGHT=0 ;;
  --macos-preflight) VERIFY_ONLY=1; MACOS_PREFLIGHT=1 ;;
  "") VERIFY_ONLY=0; MACOS_PREFLIGHT=0 ;;
  *) usage >&2; exit 2 ;;
esac
if [ "$#" -gt 1 ]; then usage >&2; exit 2; fi
if [ "$(id -u)" -eq 0 ]; then
  echo "VIA must be installed as the intended ordinary user, not root." >&2
  exit 1
fi
for cmd in uname tar curl awk mktemp; do
  if ! command -v "$cmd" >/dev/null 2>&1; then
    echo "Required tool not found: $cmd" >&2
    exit 1
  fi
done
machine="$(uname -m)"
case "$machine" in
  x86_64|amd64) arch=x64 ;;
  aarch64|arm64) arch=arm64 ;;
  *) echo "Unsupported processor: $machine" >&2; exit 1 ;;
esac
case "$(uname -s)" in
  Linux)
    platform=linux; extension=tar.xz
    case "$arch" in
      x64) expected="fd8e59d5a511510f6a298afb548f18c7d2b1be404d8b4a27d94fbe49f56cb2d6" ;;
      arm64) expected="6ad1325edbdb5649c379b75a237147a666c95d4f9ae8d340fef2d1575d289ad2" ;;
    esac
    ;;
  Darwin)
    platform=darwin; extension=tar.gz
    case "$arch" in
      x64) expected="1462cb3b3046b815cf8ea436d3da450ec1a9f11dac7e5a46b0ada5305d7e8097" ;;
      arm64) expected="bed7eea5325e1108f32ce5228ddd6a5f0f08a499ee42aa7442aea583702f6057" ;;
    esac
    ;;
  *) echo "This installer runs only on Linux and macOS." >&2; exit 1 ;;
esac
# Read-only native environment check for the free macOS Terminal/LaunchAgent
# track. A preflight pass is not an Apple Gatekeeper endorsement of
# browser-downloaded apps or quarantined native components.
check_macos_user_session() {
  if [ "$platform" != darwin ]; then
    echo "The macOS preflight must run on a real Mac." >&2
    return 1
  fi
  for required in security launchctl shasum; do
    if ! command -v "$required" >/dev/null 2>&1; then
      echo "Missing macOS prerequisite: $required" >&2
      return 1
    fi
  done
  if ! launchctl print "gui/$(id -u)" >/dev/null 2>&1; then
    echo "No active macOS GUI user launchd session; sign in to your Mac and retry." >&2
    return 1
  fi
  if ! security list-keychains -d user >/dev/null 2>&1; then
    echo "macOS user Keychain is unavailable; unlock your login session and retry." >&2
    return 1
  fi
  echo "[VIA] macOS user LaunchAgent session and Keychain command are available."
  echo "[VIA] Terminal-only installer: VIA release signatures are checked; Apple notarization is not provided."
}

if [ "$MACOS_PREFLIGHT" -eq 1 ]; then
  check_macos_user_session
  exit 0
fi
if [ "$platform" = darwin ] && [ "$VERIFY_ONLY" -eq 0 ]; then
  check_macos_user_session
fi

filename="node-${NODE_VERSION}-${platform}-${arch}.${extension}"
url="${BASE_URL}/${filename}"
if [ -n "${XDG_DATA_HOME:-}" ]; then
  case "$XDG_DATA_HOME" in
    /*) base="$XDG_DATA_HOME/via/quick-setup" ;;
    *) echo "XDG_DATA_HOME must be absolute" >&2; exit 1 ;;
  esac
elif [ "$platform" = darwin ]; then
  base="$HOME/Library/Application Support/VIA/QuickSetup"
else
  base="$HOME/.local/share/via/quick-setup"
fi
runtime="${base}/node-${NODE_VERSION}-${platform}-${arch}"
node="${runtime}/bin/node"
npx_js="${runtime}/lib/node_modules/npm/bin/npx-cli.js"
archive_cache="${base}/${filename}"
mkdir -p "$base"
chmod 700 "$base"

# Do not trust a previously extracted Node/npm directory via a writable marker.
# The per-user lock is held through the CLI invocation.
lock="${base}/.quick-setup.lock"
if ! mkdir "$lock" 2>/dev/null; then
  echo "Another VIA Quick setup is already running (or left a stale lock)." >&2
  exit 1
fi
stage=""
dependency_stage=""
cleanup() {
  if [ -n "$stage" ]; then rm -rf "$stage"; fi
  if [ -n "$dependency_stage" ]; then rm -rf "$dependency_stage"; fi
  rmdir "$lock" 2>/dev/null || :
}
trap cleanup EXIT
trap 'exit 130' INT
trap 'exit 143' TERM

hash_file() {
  if command -v sha256sum >/dev/null 2>&1; then
    sha256sum "$1" | awk '{print $1}'
  elif command -v shasum >/dev/null 2>&1; then
    shasum -a 256 "$1" | awk '{print $1}'
  else
    echo "SHA-256 tool missing." >&2
    return 1
  fi
}

# Provision the distro-verified Secret Service command privately for ordinary
# Ubuntu/Debian users without sudo or modifications to system packages.
ensure_linux_secret_tool() {
  if [ "${VIA_LINUX_SYSTEMD_CREDENTIAL_STORE:-}" = 1 ]; then return 0; fi
  if command -v secret-tool >/dev/null 2>&1; then
    secret_command="$(command -v secret-tool)"
  else
    for cmd in apt-cache apt-get dpkg-deb systemctl sha256sum install cmp sed; do
      if ! command -v "$cmd" >/dev/null 2>&1; then
        echo "VIA requires a desktop Secret Service and libsecret-tools (missing: $cmd). Install your distribution's libsecret-tools package and retry." >&2
        return 1
      fi
    done
    if ! systemctl --user show-environment >/dev/null 2>&1; then
      echo "VIA needs an active ordinary-user systemd session for secure Linux setup." >&2
      return 1
    fi
    # Match the binary package against the distro's authenticated apt index.
    package_hash="$(apt-cache show --no-all-versions libsecret-tools 2>/dev/null | awk '$1 == "SHA256:" {print $2; exit}')"
    if ! printf '%s\n' "$package_hash" | grep -Eq '^[a-fA-F0-9]{64}$'; then
      echo "Trusted libsecret-tools digest unavailable in apt indexes. Update trusted apt indexes or install libsecret-tools normally." >&2
      return 1
    fi
    dependency_stage="$(mktemp -d "$base/.dependency.XXXXXXXX")"
    if ! (cd "$dependency_stage" && apt-get download libsecret-tools >&2); then
      echo "Unable to download libsecret-tools from configured distribution repositories." >&2
      return 1
    fi
    set -- "$dependency_stage"/libsecret-tools_*.deb
    if [ "$#" -ne 1 ] || [ ! -f "$1" ]; then
      echo "Unexpected libsecret-tools package download layout." >&2
      return 1
    fi
    if [ "$(hash_file "$1")" != "$package_hash" ]; then
      echo "libsecret-tools SHA-256 mismatch with trusted apt metadata; refusing to run." >&2
      return 1
    fi
    mkdir -p "$dependency_stage/extracted"
    dpkg-deb --extract "$1" "$dependency_stage/extracted"
    candidate_secret="$dependency_stage/extracted/usr/bin/secret-tool"
    if [ ! -f "$candidate_secret" ] || [ -L "$candidate_secret" ]; then
      echo "Official libsecret-tools archive lacks the expected executable." >&2
      return 1
    fi
    private_root="$base""-deps"
    private_bin="$private_root/bin"
    if [ -L "$private_root" ] || [ -L "$private_bin" ] || [ -L "$private_bin/secret-tool" ]; then
      echo "Unsafe VIA private prerequisite path (symbolic link)." >&2
      return 1
    fi
    mkdir -p "$private_bin"
    chmod 700 "$private_root" "$private_bin"
    install -m 755 "$candidate_secret" "$private_bin/secret-tool"
    if [ "$(hash_file "$candidate_secret")" != "$(hash_file "$private_bin/secret-tool")" ]; then
      echo "Local Secret Service helper integrity check failed." >&2
      return 1
    fi
    secret_command="$private_bin/secret-tool"
    PATH="$private_bin:$PATH"
    export PATH
    echo "[VIA] Prepared trusted libsecret-tools locally (no sudo required)."

    # Persist PATH for the VIA user service only, not globally.
    config_home="$(printenv XDG_CONFIG_HOME 2>/dev/null || :)"
    if [ -n "$config_home" ]; then
      case "$config_home" in
        /*) : ;;
        *) echo "XDG_CONFIG_HOME must be absolute." >&2; return 1 ;;
      esac
    else
      config_home="$HOME/.config"
    fi
    unit_dir="$config_home/systemd/user/via-agent.service.d"
    unit_file="$unit_dir/10-via-secret-tool-path.conf"
    if [ -L "$unit_dir" ] || [ -L "$unit_file" ]; then
      echo "Unsafe symbolic-link VIA user service configuration." >&2
      return 1
    fi
    manager_path="$(systemctl --user show-environment | sed -n 's/^PATH=//p' | head -n 1)"
    case "$manager_path" in
      /*) : ;;
      *) echo "Unable to read user systemd PATH." >&2; return 1 ;;
    esac
    service_path="$private_bin:$manager_path"
    case "$service_path" in
      *'
'*) echo "Unsupported newline in Linux user path." >&2; return 1 ;;
    esac
    escaped_path="$(printf '%s' "$service_path" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/%/%%/g')"
    mkdir -p "$unit_dir"
    chmod 700 "$unit_dir"
    unit_new="$(mktemp "$unit_dir/.via-secret-tool.XXXXXXXX")"
    printf '[Service]\nEnvironment="PATH=%s"\n' "$escaped_path" > "$unit_new"
    chmod 600 "$unit_new"
    if [ -e "$unit_file" ]; then
      if ! cmp -s "$unit_file" "$unit_new"; then
        rm -f "$unit_new"
        echo "Existing VIA service PATH configuration differs; review before retrying." >&2
        return 1
      fi
      rm -f "$unit_new"
    else
      mv "$unit_new" "$unit_file"
    fi
    systemctl --user daemon-reload || return 1
  fi
  if [ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]; then
    echo "VIA requires an active desktop Secret Service session (no D-Bus address)." >&2
    return 1
  fi
  if command -v timeout >/dev/null 2>&1; then
    if ! timeout 15 "$secret_command" search service abr.via.agent >/dev/null 2>&1; then
      echo "Secret Service unavailable or locked. Unlock your desktop keyring and retry." >&2
      return 1
    fi
  elif ! "$secret_command" search service abr.via.agent >/dev/null 2>&1; then
    echo "Secret Service unavailable or locked. Unlock your desktop keyring and retry." >&2
    return 1
  fi
}

if [ -L "$archive_cache" ]; then rm "$archive_cache"; fi
if [ ! -f "$archive_cache" ] || [ "$(hash_file "$archive_cache")" != "$expected" ]; then
  echo "[VIA] Downloading official Node.js ${NODE_VERSION} (${platform}/${arch})..."
  stage="$(mktemp -d "${base}/.incoming.XXXXXXXX")"
  curl --proto '=https' --tlsv1.2 --fail --location --silent --show-error --retry 2 \
    --output "${stage}/${filename}" "$url"
  if [ "$(hash_file "${stage}/${filename}")" != "$expected" ]; then
    echo "Node.js checksum verification failed; refusing to execute." >&2
    exit 1
  fi
  mv -f "${stage}/${filename}" "$archive_cache"
  rm -rf "$stage"
  stage=""
fi

# Rebuild the runtime from the pinned, verified archive on every invocation.
# This detects modification of the cached Node executable, npm or its modules.
stage="$(mktemp -d "${base}/.incoming.XXXXXXXX")"
mkdir -p "${stage}/payload"
case "$extension" in
  tar.xz) tar -xJf "$archive_cache" -C "${stage}/payload" --strip-components=1 ;;
  tar.gz) tar -xzf "$archive_cache" -C "${stage}/payload" --strip-components=1 ;;
esac
candidate="${stage}/payload"
[ -f "${candidate}/bin/node" ] && [ -f "${candidate}/lib/node_modules/npm/bin/npx-cli.js" ] || {
  echo "Verified Node.js bundle is missing required files." >&2
  exit 1
}
[ "$("${candidate}/bin/node" --version)" = "$NODE_VERSION" ] || {
  echo "Unexpected Node.js version in verified archive." >&2
  exit 1
}
printf '%s\n' "$expected" > "${candidate}/.via-archive-sha256"
previous="${stage}/previous"
if [ -e "$runtime" ] || [ -L "$runtime" ]; then mv "$runtime" "$previous"; fi
if ! mv "$candidate" "$runtime"; then
  if [ -e "$previous" ] || [ -L "$previous" ]; then mv "$previous" "$runtime"; fi
  echo "Could not activate verified Node.js runtime." >&2
  exit 1
fi
if [ -e "$previous" ] || [ -L "$previous" ]; then rm -rf "$previous"; fi
rm -rf "$stage"
stage=""
if [ "$VERIFY_ONLY" -eq 1 ]; then
  echo "[VIA] Verified runtime ready: ${NODE_VERSION} (${platform}/${arch}); VIA setup NOT invoked."
  exit 0
fi
if [ "$platform" = linux ] && [ "$VERIFY_ONLY" -eq 0 ]; then
  ensure_linux_secret_tool
fi
echo "[VIA] Starting official VIA bootstrap..."
# Avoid accidentally executing a local project's shadowed `via` binary.
cd "$base"
PATH="${runtime}/bin:${PATH}"
unset NODE_OPTIONS NODE_PATH NODE_EXTRA_CA_CERTS LD_PRELOAD LD_LIBRARY_PATH DYLD_INSERT_LIBRARIES
NODE_TLS_REJECT_UNAUTHORIZED=1
npm_config_cache="${base}/npm-cache"
npm_config_userconfig=/dev/null
npm_config_registry="https://registry.npmjs.org/"
npm_config_strict_ssl=true
export PATH NODE_TLS_REJECT_UNAUTHORIZED npm_config_cache npm_config_userconfig npm_config_registry npm_config_strict_ssl
"$node" "$npx_js" --yes "$VIA_PACKAGE" setup
