#!/bin/sh
# PLOMID install script.
#
# Installs the PLOMID binary for macOS or Linux from GitHub Releases:
#
#   curl -fsSL https://plomid.in/install.sh | sh
#
# Read this file before you run it — that is the point of serving it from
# this site instead of piping something you cannot inspect.
#
# Release wiring: the three script files below mirror `releaseAssets` in
# `src/data/download.ts` — that file is the source of truth. If a filename
# changes there, change it here too (a static shell script cannot import
# TypeScript). Keep `VERSION` below in sync with `downloadVersion` there.
#
# What it does:
#   1. Detects OS (macOS / Linux) and architecture (arm64 / x86_64).
#   2. Downloads the matching artefact from github.com/plomid/plomid/releases:
#        macOS arm64  -> plomid-v<V>-macos-arm64.dmg   (mounted, binary copied out)
#        macOS x86_64 -> plomid-v<V>-macos-x64.dmg
#        Linux x86_64 -> plomid-v<V>-linux-x64.tar.gz  (extracted)
#        Linux arm64  -> plomid-v<V>-linux-arm64.tar.gz (extracted)
#   3. Verifies the SHA256 checksum from SHA256SUMS.txt on the same release.
#   4. Installs the `plomid-server` binary where it runs by name:
#      PLOMID_INSTALL_DIR if set, else the first of /usr/local/bin,
#      /opt/homebrew/bin, $HOME/.local/bin that is already on your PATH.
#      If none is usable you get the exact PATH fix, nothing is edited
#      behind your back. The script never starts the server — it only runs
#      `plomid-server --version` once to prove the install, then exits.
#
# What it does NOT do:
#   - No sudo unless you ask for it (only needed when the target directory
#     is not writable by you).
#   - No Windows support: on Windows download the signed .exe from the
#     releases page instead (see the download page for the current version).
#
# Options (environment or flags):
#   PLOMID_VERSION=vX.Y.Z  Install a different tag (default: v0.1.0-beta.1,
#                          keep in sync with `downloadVersion` in download.ts).
#                          Accepts "1.2.3" or "v1.2.3".
#   PLOMID_INSTALL_DIR=DIR
#                         Install directory (default: first writable of
#                         /usr/local/bin, $HOME/.local/bin).
#   PLOMID_REPO=owner/repo
#                         Override the GitHub repo (default: plomid/plomid).
#
#   sh install.sh --help     Show this help.
#   sh install.sh --dry-run  Print what would be downloaded, change nothing.
#   sh install.sh --list     List published versions, then exit.
#   sh install.sh --no-color Disable colors.
#
# Colors follow the site palette (copper on ink) when stdout is a terminal;
# piped or logged output stays plain. NO_COLOR=1 or PLOMID_COLOR=0 disables
# them, PLOMID_COLOR=1 forces them.
#
# Exit codes: 0 installed (or dry-run), 1 usage/error, 2 unsupported platform.

set -eu

REPO="${PLOMID_REPO:-plomid/plomid}"
VERSION="${PLOMID_VERSION:-v0.1.0-beta.1}"
INSTALL_DIR="${PLOMID_INSTALL_DIR:-}"
DRY_RUN=0
LIST=0
NO_COLOR_FLAG=0

C_BOLD=""; C_DIM=""; C_COPPER=""; C_GREEN=""; C_RED=""; C_YELLOW=""; C_RESET=""

log() { printf '%s\n' "$*"; }
step() { printf '%s▸%s %s\n' "$C_COPPER" "$C_RESET" "$*"; }
ok() { printf '%s✓%s %s\n' "$C_GREEN" "$C_RESET" "$*"; }
warn() { printf '%splomid-install:%s %s\n' "$C_YELLOW" "$C_RESET" "$*" >&2; }
die() { printf '%s✗ plomid-install:%s %s\n' "$C_RED" "$C_RESET" "$*" >&2; exit 1; }

setup_colors() {
  if [ "$NO_COLOR_FLAG" = "1" ] || [ -n "${NO_COLOR:-}" ] || [ "${PLOMID_COLOR:-}" = "0" ]; then
    return 0
  fi
  if [ "${PLOMID_COLOR:-}" = "1" ]; then _color=1
  elif [ -t 1 ] && [ "${TERM:-dumb}" != "dumb" ]; then _color=1
  else _color=0
  fi
  if [ "$_color" = "1" ]; then
    C_BOLD="$(printf '\033[1m')"
    C_DIM="$(printf '\033[2m')"
    C_COPPER="$(printf '\033[38;5;173m')"
    C_GREEN="$(printf '\033[32m')"
    C_RED="$(printf '\033[31m')"
    C_YELLOW="$(printf '\033[33m')"
    C_RESET="$(printf '\033[0m')"
  fi
}

banner() {
  printf '%s\n' \
"${C_COPPER}█████████▄    ██           ▄████████▄    ██▄      ▄██    █    █████████▄" \
"${C_COPPER}         █    ██          ██▀      ▀██   ████▄  ▄████    █    ▄▄       ██" \
"${C_COPPER}▄▄▄███████    ██          ██        ██   ██ ▀▀███▀ ██    █    ██       ██" \
"${C_COPPER}██            ██          ██       ▄██   ██    ▀   ██    █    ██       ██" \
"${C_COPPER}██            █████████    ▀████████▀    ██        ██    █    █████████▀${C_RESET}"
  printf '%s%s%s\n' "$C_DIM" "  Platform for Modern Intelligence and Data" "$C_RESET"
  printf '%s  installer %s · %s / %s%s\n' "$C_DIM" "$TAG" "$OS" "$ARCH" "$C_RESET"
}

spinner() {
  # spinner <pid> <message> — animates while <pid> lives (tty only),
  # then clears the line; the caller prints the result. Returns pid status.
  _spid="$1"; _smsg="$2"
  if [ ! -t 1 ]; then
    printf '%s…\n' "$_smsg"
    wait "$_spid"
    return $?
  fi
  _i=0
  while kill -0 "$_spid" 2>/dev/null; do
    _i=$((_i % 4 + 1))
    _f="$(printf '%s' '/-\|' | cut -c "$_i")"
    printf '\r%s%s%s %s' "$C_COPPER" "$_f" "$C_RESET" "$_smsg"
    sleep 0.1
  done
  printf '\r%64s\r' ' '
  wait "$_spid"
  return $?
}

usage() {
  # Print the header comment (lines 2-56) without the leading `#`.
  sed -n '2,56p' "$0" 2>/dev/null | sed 's/^# \{0,1\}//'
}

# --- args ---------------------------------------------------------------
while [ "$#" -gt 0 ]; do
  case "$1" in
    -h|--help) usage; exit 0 ;;
    --dry-run) DRY_RUN=1; shift ;;
    --list|--versions) LIST=1; shift ;;
    --no-color) NO_COLOR_FLAG=1; shift ;;
    --version) VERSION="${2:?--version needs a value}"; shift 2 ;;
    --version=*) VERSION="${1#--version=}"; shift ;;
    --dir) INSTALL_DIR="${2:?--dir needs a value}"; shift 2 ;;
    --dir=*) INSTALL_DIR="${1#--dir=}"; shift ;;
    --) shift; break ;;
    -*) die "unknown flag: $1 (see --help)" ;;
    *) break ;;
  esac
done

setup_colors

# --- download tool ------------------------------------------------------
have() { command -v "$1" >/dev/null 2>&1; }

downloader=""
if have curl; then
  downloader="curl"
elif have wget; then
  downloader="wget"
else
  die "need curl or wget to download the release"
fi

fetch() {
  # fetch <url> <dest>
  if [ "$downloader" = "curl" ]; then
    curl -fsSL --retry 3 --proto '=https' --tlsv1.2 "$1" -o "$2"
  else
    wget -q --tries=3 --https-only "$1" -O "$2"
  fi
}

# --- platform -----------------------------------------------------------
OS="$(uname -s 2>/dev/null || echo unknown)"
ARCH="$(uname -m 2>/dev/null || echo unknown)"

# Tag with leading v, whatever the caller passed.
TAG="v${VERSION#v}"

ASSET=""
KIND=""
case "$OS" in
  Darwin)
    case "$ARCH" in
      arm64|aarch64) ASSET="plomid-${TAG}-macos-arm64.dmg"; KIND="dmg" ;;
      x86_64|amd64)  ASSET="plomid-${TAG}-macos-x64.dmg"; KIND="dmg" ;;
      *) die "unsupported macOS architecture: $ARCH" ;;
    esac
    ;;
  Linux)
    case "$ARCH" in
      x86_64|amd64) ASSET="plomid-${TAG}-linux-x64.tar.gz"; KIND="tarball" ;;
      arm64|aarch64) ASSET="plomid-${TAG}-linux-arm64.tar.gz"; KIND="tarball" ;;
      *) die "unsupported Linux architecture: $ARCH" ;;
    esac
    ;;
  MINGW*|MSYS*|CYGWIN*|Windows*)
    warn "this script does not install on Windows."
    warn "Download the signed installer instead:"
    warn "  https://github.com/${REPO}/releases"
    exit 2
    ;;
  *)
    die "unsupported OS: $OS (macOS and Linux only; Windows uses the .exe on the releases page)"
    ;;
esac

# --- version list ---------------------------------------------------------
list_versions() {
  _api="https://api.github.com/repos/${REPO}/releases?per_page=15"
  _ld="$(mktemp -d 2>/dev/null || mktemp -d -t plomid-list)"
  _jf="${_ld}/releases.json"
  fetch "$_api" "$_jf" || { rm -rf "$_ld"; die "could not reach the GitHub API: $_api"; }
  _tags="$(grep -o '\"tag_name\": *\"[^\"]*\"' "$_jf" | sed 's/^\"tag_name\": *\"//;s/\"$//')"
  _dates="$(grep -o '\"published_at\": *\"[^\"]*\"' "$_jf" | sed 's/^\"published_at\": *\"//;s/\"$//;s/T.*$//')"
  rm -f "$_jf"
  if [ -z "$_tags" ]; then rm -rf "$_ld"; die "no published releases found for ${REPO}"; fi
  printf '%sReleases for %s:%s\n' "$C_BOLD" "$REPO" "$C_RESET"
  printf '%s\n' "$_tags" > "$_ld/tags"
  printf '%s\n' "$_dates" > "$_ld/dates"
  paste -d '|' "$_ld/tags" "$_ld/dates" | while IFS='|' read -r _t _d; do
    if [ "$_t" = "$TAG" ]; then _mark=" ${C_COPPER}← default${C_RESET}"; else _mark=""; fi
    printf '  %s%s%s  %s%s%s%s\n' "$C_GREEN" "$_t" "$C_RESET" "$C_DIM" "$_d" "$C_RESET" "$_mark"
  done
  rm -rf "$_ld"
  printf '\ninstall one with:  sh install.sh --version <tag>\n'
}

# --- version → URL ------------------------------------------------------
BASE="https://github.com/${REPO}/releases/download/${TAG}"
URL="${BASE}/${ASSET}"
SUM_URL="${BASE}/SHA256SUMS.txt"

if [ "$LIST" = "1" ]; then
  list_versions
  exit 0
fi

# --- install dir --------------------------------------------------------
# The binary must run by name afterwards, so prefer a directory that is both
# usable and already on PATH. Nothing outside the chosen directory is touched.
pick_dir() {
  if [ -n "$INSTALL_DIR" ]; then printf '%s' "$INSTALL_DIR"; return; fi
  for d in /usr/local/bin /opt/homebrew/bin "$HOME/.local/bin"; do
    case ":${PATH-}:" in
      *":${d}:"*) ;;
      *) continue ;;
    esac
    if [ -d "$d" ] && [ -w "$d" ]; then printf '%s' "$d"; return; fi
    if [ ! -e "$d" ]; then
      parent="$(dirname "$d")"
      if [ -d "$parent" ] && [ -w "$parent" ]; then printf '%s' "$d"; return; fi
    fi
  done
  # Last resort: user-local bin. The PATH fix is printed after installing.
  printf '%s' "$HOME/.local/bin"
}
DEST_DIR="$(pick_dir)"
BIN_DEST="${DEST_DIR}/plomid-server"

banner
printf '%splatform:%s %s / %s    %starget:%s %s\n' "$C_DIM" "$C_RESET" "$OS" "$ARCH" "$C_DIM" "$C_RESET" "$BIN_DEST"
printf '%sasset:%s    %s\n' "$C_DIM" "$C_RESET" "$ASSET"
printf '%ssource:%s   %s\n' "$C_DIM" "$C_RESET" "$URL"

if [ "$DRY_RUN" = "1" ]; then
  log "dry-run: nothing downloaded, nothing installed."
  exit 0
fi

# --- work dir -----------------------------------------------------------
TMP="$(mktemp -d 2>/dev/null || mktemp -d -t plomid-install)"
cleanup() { rm -rf "$TMP"; }
trap cleanup EXIT INT TERM
FILE="${TMP}/${ASSET}"

step "Download"
fetch "$URL" "$FILE" &
spinner "$!" "Downloading ${ASSET}" || die "download failed: $URL"
ok "Downloaded ${ASSET}"

# --- checksum from the release manifest -----------------------------------
SUM_FILE="${TMP}/SHA256SUMS.txt"
if fetch "$SUM_URL" "$SUM_FILE" 2>/dev/null; then
  # manifest lines look like `<hash>[ *]<filename>`
  EXPECTED="$(awk -v f="$ASSET" '$2 == f || $2 == "*" f {print $1}' "$SUM_FILE" | head -n 1 | tr -d ' \r\n')"
  if [ -n "$EXPECTED" ]; then
    ACTUAL=""
    if have shasum; then
      ACTUAL="$(shasum -a 256 "$FILE" | awk '{print $1}')"
    elif have sha256sum; then
      ACTUAL="$(sha256sum "$FILE" | awk '{print $1}')"
    else
      warn "downloaded, but no shasum/sha256sum found — skipping verification."
      warn "compare manually with SHA256SUMS.txt on https://github.com/${REPO}/releases"
    fi
    if [ -n "${ACTUAL:-}" ]; then
      if [ "$ACTUAL" = "$EXPECTED" ]; then
        ok "Checksum verified (sha256, from SHA256SUMS.txt)."
      else
        die "checksum mismatch — deleted, do not run it. Expected ${EXPECTED}, got ${ACTUAL}."
      fi
    fi
  else
    warn "SHA256SUMS.txt has no entry for ${ASSET} — continuing unverified."
    warn "compare manually: shasum -a 256 <file> (macOS) or sha256sum <file> (Linux)"
  fi
else
  warn "could not download SHA256SUMS.txt — continuing unverified."
  warn "you can still verify manually: shasum -a 256 <file> (macOS) or sha256sum <file> (Linux)"
  warn "against SHA256SUMS.txt on https://github.com/${REPO}/releases"
fi

# --- extract ------------------------------------------------------------
SRC_BIN=""
case "$KIND" in
  tarball)
    tar -xzf "$FILE" -C "$TMP" || die "could not unpack $ASSET"
    # the tarball carries a top-level `plomid` binary (possibly nested one level)
    SRC_BIN="$(find "$TMP" -maxdepth 3 -type f -name plomid-server -perm +111 2>/dev/null | head -n 1 || true)"
    if [ -z "$SRC_BIN" ]; then
      SRC_BIN="$(find "$TMP" -maxdepth 3 -type f -name plomid-server 2>/dev/null | head -n 1 || true)"
    fi
    [ -n "$SRC_BIN" ] || die "archive did not contain a plomid-server binary"
    ;;
  dmg)
    have hdiutil || die "hdiutil not found — cannot mount the .dmg. Download it manually from $URL"
    MNT="$(mktemp -d /tmp/plomid-mnt.XXXXXX 2>/dev/null || mktemp -d -t plomid-mnt)"
    hdiutil attach -nobrowse -readonly -mountpoint "$MNT" "$FILE" >/dev/null \
      || die "could not mount $ASSET"
    # dmg layouts vary: prefer a bare binary, then .app/Contents/MacOS/plomid
    SRC_BIN="$(find "$MNT" -maxdepth 3 -type f -name plomid-server -perm +111 2>/dev/null | head -n 1 || true)"
    if [ -z "$SRC_BIN" ]; then
      SRC_BIN="$(find "$MNT" -maxdepth 4 -path '*.app/Contents/MacOS/*' -type f -perm +111 2>/dev/null | head -n 1 || true)"
    fi
    if [ -z "$SRC_BIN" ]; then
      hdiutil detach "$MNT" >/dev/null 2>&1 || true
      die "mounted the .dmg but found no plomid-server binary inside"
    fi
    cp "$SRC_BIN" "${TMP}/plomid-server" || { hdiutil detach "$MNT" >/dev/null 2>&1 || true; die "could not copy binary out of the .dmg"; }
    hdiutil detach "$MNT" >/dev/null 2>&1 || true
    rmdir "$MNT" 2>/dev/null || true
    SRC_BIN="${TMP}/plomid-server"
    ;;
esac
chmod +x "$SRC_BIN"

# --- install ------------------------------------------------------------
mkdir -p "$DEST_DIR" || die "could not create $DEST_DIR"
if [ -w "$DEST_DIR" ]; then
  if have install; then
    install -m 0755 "$SRC_BIN" "$BIN_DEST"
  else
    cp "$SRC_BIN" "$BIN_DEST" && chmod 0755 "$BIN_DEST"
  fi
else
  warn "$DEST_DIR is not writable — retrying with sudo."
  have sudo || die "need sudo to write to $DEST_DIR, or set PLOMID_INSTALL_DIR to a writable directory"
  sudo mkdir -p "$DEST_DIR"
  if have install; then
    sudo install -m 0755 "$SRC_BIN" "$BIN_DEST"
  else
    sudo cp "$SRC_BIN" "$BIN_DEST" && sudo chmod 0755 "$BIN_DEST"
  fi
fi

# --- verify -------------------------------------------------------------
if [ -x "$BIN_DEST" ]; then
  ok "Installed to ${BIN_DEST}"
else
  die "copy finished but ${BIN_DEST} is not executable"
fi

case ":$PATH:" in
  *":${DEST_DIR}:"*) ;;
  *)
    warn "'${DEST_DIR}' is not on your PATH, so 'plomid-server' will not run by name yet."
    warn "run this now:  export PATH=\"${DEST_DIR}:\$PATH\""
    case "${SHELL:-}" in
      *fish) warn "make it permanent:  fish_add_path ${DEST_DIR}" ;;
      *bash) warn "make it permanent, then restart your terminal:"; warn "  echo 'export PATH=\"${DEST_DIR}:\$PATH\"' >> ~/.bashrc" ;;
      *) warn "make it permanent, then restart your terminal:"; warn "  echo 'export PATH=\"${DEST_DIR}:\$PATH\"' >> ~/.zshrc" ;;
    esac
    ;;
esac

log "version check (prints the version, starts nothing):"
if "$BIN_DEST" --version >/dev/null 2>&1; then
  "$BIN_DEST" --version 2>/dev/null || true
fi

log ""
printf '%sNext steps:%s\n' "$C_BOLD" "$C_RESET"
log "  ${BIN_DEST}            # start the server with built-in defaults"
log "  ${BIN_DEST} --help    # every flag and PLOMID_* variable"
log "  Docs: https://plomid.in/docs/  # flags, connection details, examples"
log ""
log "Prefer Docker?  docker run -d -p 5432:5432 plomid/plomid:latest"
