#!/bin/sh
# noirdeck installer — https://noirdeck.dev
#
# This script is piped into a shell by strangers, so it is written to be READ:
# short, no sudo, no surprises, and it tells you what it will do before doing it.
#
#   curl -fsSL https://noirdeck.dev/install.sh | sh -s -- YOUR-DOWNLOAD-KEY
#
# What it does, in order:
#   1. refuses to run where noirdeck cannot work (native Windows)
#   2. installs `uv` into your home directory if it is missing (no admin rights)
#   3. downloads the wheel from noirdeck.dev using your key, and installs it as a
#      uv tool, which brings its own Python
#   4. tells you the two commands worth knowing
#
# It does NOT: use sudo, write outside your home directory, add anything to your
# shell profile without saying so, or send anything anywhere.
#
# POSIX sh on purpose — /bin/sh on a fresh Debian is dash, not bash.

set -eu

RED=''
DIM=''
BOLD=''
OFF=''
if [ -t 1 ]; then
  RED=$(printf '\033[31m')
  DIM=$(printf '\033[2m')
  BOLD=$(printf '\033[1m')
  OFF=$(printf '\033[0m')
fi

say() { printf '%s\n' "$*"; }
die() {
  printf '%s\n' "${RED}noirdeck:${OFF} $*" >&2
  exit 1
}

# ── 1. is this a machine noirdeck can actually run on? ──────────────────────
# ⛔ Not a preference. The workspace terminal uses pty.fork(), termios and fcntl,
# which are POSIX-only and imported at module load — so on native Windows noirdeck
# fails at import, before it can serve anything. There is nothing to install there.
# Under WSL it is Linux and this script is correct.
case "$(uname -s 2>/dev/null || echo unknown)" in
  Linux | Darwin) ;;
  MINGW* | MSYS* | CYGWIN*)
    die "native Windows is not supported — noirdeck needs a POSIX pty.
      Install it inside WSL instead:  wsl -d Ubuntu, then re-run this command."
    ;;
  *) die "unrecognised platform '$(uname -s)'. noirdeck supports Linux and macOS." ;;
esac

say ""
say "${BOLD}noirdeck${OFF} — local build console. Installing into your home directory."
say "${DIM}No admin rights needed. Nothing is written outside \$HOME.${OFF}"
say ""

# ── 2. uv, if it is missing ────────────────────────────────────────────────
# uv is used because it brings its OWN Python. Requiring a specific system Python
# is the friction that makes a commercial tool feel like a dev script.
if command -v uv >/dev/null 2>&1; then
  say "  uv        already installed ($(uv --version 2>/dev/null || echo unknown))"
else
  say "  uv        not found — installing from astral.sh"
  if ! command -v curl >/dev/null 2>&1; then
    die "curl is required to install uv. Install curl, or install uv yourself: https://astral.sh/uv"
  fi
  # astral's own installer; user-local, no sudo
  curl -fsSL https://astral.sh/uv/install.sh | sh >/dev/null 2>&1 ||
    die "could not install uv. Install it yourself and re-run: https://astral.sh/uv"
  # the installer puts it here but may not have touched this shell's PATH yet
  for d in "$HOME/.local/bin" "$HOME/.cargo/bin"; do
    [ -x "$d/uv" ] && PATH="$d:$PATH"
  done
  export PATH
  command -v uv >/dev/null 2>&1 || die "uv installed but is not on PATH. Add \$HOME/.local/bin to PATH and re-run."
  say "  uv        installed"
fi

# ── 3. noirdeck ────────────────────────────────────────────────────────────
# noirdeck is distributed from noirdeck.dev rather than a public index, so the
# download carries your key. That is the ONLY place a key is checked: the
# installed software never contacts us, and needs no key to run.
# The key can come from the environment or from the first argument. Argument is
# the default because it is what makes a one-line install possible, and it has a
# cost worth knowing: an argument is visible in `ps` to other users on the same
# machine, and lands in shell history when typed rather than piped. On a shared
# machine prefer:
#
#   NOIRDECK_KEY=... sh -c "$(curl -fsSL https://noirdeck.dev/install.sh)"
#
# The key downloads free Early Access software rather than authorising a payment,
# so this is worth stating and not worth contorting the install for.
[ -n "${NOIRDECK_KEY:-}" ] || NOIRDECK_KEY="${1:-}"
if [ -z "$NOIRDECK_KEY" ]; then
  die "this installer needs your download key:

      curl -fsSL https://noirdeck.dev/install.sh | sh -s -- YOUR-KEY

      Keys are free during Early Access. Get one at https://noirdeck.dev/signup"
fi

say "  noirdeck  downloading"
WHEEL_DIR="$(mktemp -d)"
# shellcheck disable=SC2064
trap "rm -rf '$WHEEL_DIR'" EXIT INT TERM

# NO -f here, on purpose. With --fail curl exits non-zero on a 403 and the status
# never reaches the case below, so every refused key would report as a generic
# failure. Without it, the body lands in a file we ignore and the code is real.
HTTP="$(curl -sSL -D "$WHEEL_DIR/head" -o "$WHEEL_DIR/body" -w '%{http_code}' \
  "https://noirdeck.dev/dl/$NOIRDECK_KEY/latest" 2>/dev/null || echo 000)"
case "$HTTP" in
  200) ;;
  403) die "that key was refused. If it was working before it may have been revoked:
      reply to hello@noirdeck.dev and we will sort it out." ;;
  404) die "no artifact for that request. Check https://noirdeck.dev for the current version." ;;
  000) die "could not reach noirdeck.dev. Check your connection and try again." ;;
  *)   die "download failed (HTTP $HTTP). Try again, or email hello@noirdeck.dev." ;;
esac
[ -s "$WHEEL_DIR/body" ] || die "the download was empty. Try again, or email hello@noirdeck.dev."

# The filename matters: a wheel must be named {name}-{version}-{py}-{abi}-{plat}.whl
# or the installer refuses it before it ever reads what is inside. The server
# sends the real name in Content-Disposition, so take it from there rather than
# teaching this script which version it is fetching.
WHEEL_NAME="$(sed -n 's/.*[Ff]ilename="\([^"]*\)".*/\1/p' "$WHEEL_DIR/head" | tr -d '\r' | tail -1)"
case "$WHEEL_NAME" in
  *.whl) ;;
  *) die "the server did not say what it sent. Try again, or email hello@noirdeck.dev." ;;
esac
# Strip any path the header might carry: this becomes a filename we write to.
WHEEL_NAME="$(basename "$WHEEL_NAME")"
WHEEL="$WHEEL_DIR/$WHEEL_NAME"
mv "$WHEEL_DIR/body" "$WHEEL"

say "  noirdeck  installing"
uv tool install --quiet --force "$WHEEL" ||
  die "install failed after a successful download. Send the output above to hello@noirdeck.dev."

BIN="$(command -v noirdeck 2>/dev/null || true)"
if [ -z "$BIN" ]; then
  for d in "$HOME/.local/bin" "$HOME/.local/share/uv/tools/noirdeck/bin"; do
    [ -x "$d/noirdeck" ] && BIN="$d/noirdeck" && break
  done
fi
[ -n "$BIN" ] || die "installed, but the 'noirdeck' command is not on PATH. Add \$HOME/.local/bin to PATH."

say ""
say "  ${BOLD}installed${OFF} — $("$BIN" --version 2>/dev/null || echo noirdeck)"
say ""

# ── 4. the two commands worth knowing ──────────────────────────────────────
say "  ${BOLD}noirdeck${OFF}              open the console"
say "  ${BOLD}noirdeck do \"...\"${OFF}     give it a task from here, no browser"
say ""
say "  ${DIM}noirdeck doctor        check what this machine can do"
say "  noirdeck status        is anything wrong${OFF}"
say ""
say "  ${DIM}Free for personal use, and free for business use during Early Access."
say "  Your key was used to download. It is not needed to run.${OFF}"
say ""

case ":$PATH:" in
  *":$HOME/.local/bin:"*) ;;
  *) say "  ${RED}note${OFF} add this to your shell profile so the command persists:"
     say "       export PATH=\"\$HOME/.local/bin:\$PATH\""
     say "" ;;
esac
