#!/bin/sh
# dollama installer for macOS and Linux
# Usage: curl -fsSL https://dollama.net/install.sh | sh

set -eu

BASE_URL="https://dollama.net/dl"
BINARY="dollama"
INSTALL_DIR="${DOLLAMA_INSTALL_DIR:-$HOME/.dollama/bin}"
tmp=""

# Check if we can prompt the user / drive the interactive setup TUI (handles
# curl | sh piping). /dev/tty can exist as a device node yet fail to open when
# there's no controlling terminal (containers, CI, systemd units), so actually
# try to open it rather than testing for existence.
is_interactive() {
  (: </dev/tty) 2>/dev/null
}

# Print the SHA-256 of $1 using whichever tool is available.
sha256_of() {
  if command -v sha256sum >/dev/null 2>&1; then
    sha256sum "$1" | cut -d' ' -f1
  else
    shasum -a 256 "$1" | cut -d' ' -f1
  fi
}

# Verify $1 (downloaded file) against the entry named $2 in checksums file $3.
# Verification is mandatory: a missing entry or a mismatch aborts the install.
verify_checksum() {
  _file="$1"
  _name="$2"
  _sums="$3"
  # Exact filename match on the second field (sha256sum writes "hash  name",
  # or "hash *name" in binary mode) — never a substring match.
  _want=$(awk -v f="$_name" '$2 == f || $2 == "*" f { print $1; exit }' "$_sums")
  if [ -z "$_want" ]; then
    echo "Error: checksums.txt has no entry for ${_name}." >&2
    echo "Refusing to install an unverified binary. Aborting installation." >&2
    exit 1
  fi
  _actual=$(sha256_of "$_file")
  if [ "$_actual" != "$_want" ]; then
    echo "Checksum mismatch for ${_name}! Expected: $_want  Got: $_actual" >&2
    echo "Aborting installation." >&2
    exit 1
  fi
  echo "Checksum verified."
}

# Move an existing binary aside before overwriting it. The dollama tray/daemon
# auto-starts at login, and $tmp is usually a tmpfs, so `mv` into the install
# dir is a cross-device copy that opens the destination with O_TRUNC — which
# the kernel refuses with ETXTBSY while that binary is being executed, aborting
# the whole install under `set -e`. Renaming the running binary out of the way
# first always works (the running process keeps its inode), the same trick the
# self-updater and install.ps1 use. $1 is "sudo" when the install dir needs it.
stash_running_binary() {
  _sudo="${1:-}"
  [ -e "${INSTALL_DIR}/${BINARY}" ] || return 0
  $_sudo rm -f "${INSTALL_DIR}/${BINARY}.old" 2>/dev/null || true
  $_sudo mv -f "${INSTALL_DIR}/${BINARY}" "${INSTALL_DIR}/${BINARY}.old" 2>/dev/null || true
}

main() {
  local os arch ext url version

  echo ""
  echo "========================================"
  echo "  Installing dollama"
  echo "  Free, shared LLM inference over Ollama"
  echo "========================================"
  echo ""

  os="$(detect_os)"
  arch="$(detect_arch)"
  ext="tar.gz"

  echo "Detected platform: ${os}/${arch}"

  version="$(latest_version)"
  if [ -z "$version" ]; then
    echo "Error: could not determine the latest dollama version." >&2
    echo "Check your internet connection, or download manually from https://dollama.net" >&2
    exit 1
  fi
  echo "Latest version: ${version}"

  url="${BASE_URL}/${version}/${BINARY}-${os}-${arch}.${ext}"
  echo ""
  echo "Downloading dollama ${version} ..."
  echo "  Source: ${url}"

  tmp="$(mktemp -d)"
  trap 'rm -rf "$tmp"' EXIT

  curl -fsSL "$url" -o "${tmp}/${BINARY}.${ext}"

  # Verify the SHA-256 checksum. This is mandatory: no hashing tool, a failed
  # checksums.txt download, a missing entry, or a mismatch all abort.
  checksums_url="${BASE_URL}/${version}/checksums.txt"
  if ! command -v sha256sum >/dev/null 2>&1 && ! command -v shasum >/dev/null 2>&1; then
    echo "Error: neither sha256sum nor shasum found. Cannot verify download integrity." >&2
    echo "Install one of these tools and try again." >&2
    exit 1
  fi
  echo "Verifying download integrity (SHA-256 checksum) ..."
  if ! curl -fsSL "$checksums_url" -o "${tmp}/checksums.txt"; then
    echo "Error: could not download ${checksums_url} to verify the download." >&2
    echo "Refusing to install an unverified binary. Aborting installation." >&2
    exit 1
  fi
  verify_checksum "${tmp}/${BINARY}.${ext}" "${BINARY}-${os}-${arch}.${ext}" "${tmp}/checksums.txt"

  tar -xzf "${tmp}/${BINARY}.${ext}" -C "$tmp"
  # Set the mode while the file is still ours in $tmp: after a `sudo mv` into
  # a root-owned directory an unprivileged chmod would fail under set -e.
  chmod +x "${tmp}/${BINARY}"

  echo "Installing to ${INSTALL_DIR}/${BINARY}..."
  if mkdir -p "$INSTALL_DIR" 2>/dev/null && [ -w "$INSTALL_DIR" ]; then
    stash_running_binary ""
    mv -f "${tmp}/${BINARY}" "${INSTALL_DIR}/${BINARY}"
    rm -f "${INSTALL_DIR}/${BINARY}.old" 2>/dev/null || true
  else
    sudo mkdir -p "$INSTALL_DIR"
    stash_running_binary sudo
    sudo mv -f "${tmp}/${BINARY}" "${INSTALL_DIR}/${BINARY}"
    sudo rm -f "${INSTALL_DIR}/${BINARY}.old" 2>/dev/null || true
  fi

  # macOS: strip quarantine attribute so Gatekeeper allows execution
  if [ "$os" = "darwin" ] && command -v xattr >/dev/null 2>&1; then
    xattr -cr "${INSTALL_DIR}/${BINARY}" 2>/dev/null || true
  fi

  echo "dollama ${version} installed successfully!"

  # Add to PATH if installed to a non-standard location
  if ! echo "$PATH" | tr ':' '\n' | grep -qx "$INSTALL_DIR"; then
    local path_line="export PATH=\"$INSTALL_DIR:\$PATH\""
    # Write to every profile the user actually has, zsh's and bash's alike:
    # zsh reads neither .bashrc nor .profile, so a Linux zsh user used to get
    # no PATH entry at all. Never appended twice (grep -qF guard below).
    local candidates="$HOME/.zprofile $HOME/.zshrc $HOME/.bash_profile $HOME/.bashrc"
    if [ "$os" != "darwin" ]; then
      candidates="$candidates $HOME/.profile"
    fi

    local wrote=""
    for shell_profile in $candidates; do
      [ -f "$shell_profile" ] || continue
      if grep -qF "$INSTALL_DIR" "$shell_profile" 2>/dev/null; then
        wrote="yes"
        continue
      fi
      echo "" >> "$shell_profile"
      echo "# Added by dollama installer" >> "$shell_profile"
      echo "$path_line" >> "$shell_profile"
      echo "Added $INSTALL_DIR to PATH in $shell_profile"
      wrote="yes"
    done

    if [ -z "$wrote" ]; then
      # No profile exists yet — create the one this login shell reads.
      local default_profile
      case "${SHELL:-}" in
        */zsh|zsh) default_profile="$HOME/.zprofile" ;;
        *)
          if [ "$os" = "darwin" ]; then
            default_profile="$HOME/.zprofile"
          else
            default_profile="$HOME/.profile"
          fi
          ;;
      esac
      echo "# Added by dollama installer" >> "$default_profile"
      echo "$path_line" >> "$default_profile"
      echo "Added $INSTALL_DIR to PATH in $default_profile"
    fi

    export PATH="$INSTALL_DIR:$PATH"
  fi

  # Warn about old binary in /usr/local/bin if we installed elsewhere
  if [ "$INSTALL_DIR" != "/usr/local/bin" ] && [ -f "/usr/local/bin/dollama" ]; then
    echo ""
    echo "Note: Found old dollama binary at /usr/local/bin/dollama"
    echo "To avoid conflicts, remove it with: sudo rm /usr/local/bin/dollama"
  fi

  # Create app launcher entry (Spotlight/Launchpad on macOS, XDG launcher on
  # Linux). Login auto-start is a separate, explicit choice offered inside
  # `dollama setup` below, so always skip it here (--no-login) rather than
  # silently enabling it.
  if [ "$os" = "darwin" ] || [ "$os" = "linux" ]; then
    echo "Installing app launcher..."
    # Best-effort: a launcher failure must not abort (set -e) before the
    # guided setup below — the binary itself is already installed.
    "${INSTALL_DIR}/${BINARY}" install-app --no-login ||
      echo "Warning: could not install the app launcher (you can run: dollama install-app)" >&2
  fi

  echo ""
  echo "=== dollama ${version} installed! ==="
  echo ""
  if [ "$os" = "darwin" ]; then
    echo "App launcher: ~/Applications/dollama.app (Spotlight / Launchpad)"
  fi
  echo "Dashboard: http://127.0.0.1:11436"
  echo ""
  echo "To use with Claude Code:"
  echo "  export ANTHROPIC_BASE_URL=http://127.0.0.1:11435"
  echo ""
  echo "Docs: https://dollama.net"

  # Hand off to the same guided setup that `dollama setup` runs on its own:
  # hardware detection, Ollama install, tiered model pull + benchmark,
  # classification, mode + account selection, Speech-to-Text, auto-start, and
  # an optional launch all live in cli/internal/onboard, so this script no
  # longer reimplements any of it. curl | sh consumes this script's own stdin,
  # so the TUI is fed the real terminal explicitly via /dev/tty.
  if is_interactive; then
    echo ""
    "${INSTALL_DIR}/${BINARY}" setup </dev/tty || echo "Setup did not finish — run it anytime with: dollama setup"
  else
    echo ""
    echo "Non-interactive install — skipping guided setup."
    echo "Launching dollama in the background; open the dashboard to finish setup."
    nohup "${INSTALL_DIR}/${BINARY}" app </dev/null >/dev/null 2>&1 &
    disown 2>/dev/null || true
    echo "Or run 'dollama setup' later in an interactive terminal."
  fi
}

detect_os() {
  case "$(uname -s)" in
    Linux*)  echo "linux" ;;
    Darwin*) echo "darwin" ;;
    *)       echo "Unsupported OS: $(uname -s)" >&2; exit 1 ;;
  esac
}

detect_arch() {
  _machine="$(uname -m)"
  # Under Rosetta 2 an x86_64 shell on Apple Silicon reports x86_64 from
  # `uname -m`, which would silently install the amd64 build on an arm64 Mac.
  # The kernel flags the translation, so prefer the real architecture.
  if [ "$(uname -s)" = "Darwin" ] &&
    [ "$(sysctl -n sysctl.proc_translated 2>/dev/null || echo 0)" = "1" ]; then
    _machine="arm64"
  fi
  case "$_machine" in
    x86_64|amd64)  echo "amd64" ;;
    aarch64|arm64)  echo "arm64" ;;
    *)              echo "Unsupported architecture: $(uname -m)" >&2; exit 1 ;;
  esac
}

latest_version() {
  curl -fsSL "${BASE_URL}/latest" | grep '"version"' | sed -E 's/.*"version": *"([^"]+)".*/\1/'
}

main "$@"
