#!/bin/sh
# Page installer — https://a-gnt.com/page
#
# Installs the `page` CLI to ~/.local/bin and the MCP server to
# ~/.local/share/pager.
#
# Every file is fetched to a temporary directory, checked against the SHA-256
# published in the manifest, parsed by node to prove it is valid JavaScript,
# and only then moved into place. Nothing already on disk is touched until all
# checks pass.
#
# The checksums are printed at the end. CHECKSUMS.txt in
# github.com/joey-io/pager is the second, independent copy — if the two ever
# disagree, stop and tell us.
set -eu

BASE="${PAGER_URL:-https://a-gnt.com/page}"
BIN_DIR="${PAGE_BIN_DIR:-$HOME/.local/bin}"
LIB_DIR="${PAGE_LIB_DIR:-$HOME/.local/share/pager}"

die() { printf 'error: %s\n' "$1" >&2; exit 1; }

command -v node >/dev/null 2>&1 || die "Page needs Node 18 or newer. Install node and re-run."
command -v curl >/dev/null 2>&1 || die "curl is required."

# Both spellings are common; require one of them rather than skipping the check.
if command -v sha256sum >/dev/null 2>&1; then
  sha256() { sha256sum "$1" | cut -d' ' -f1; }
elif command -v shasum >/dev/null 2>&1; then
  sha256() { shasum -a 256 "$1" | cut -d' ' -f1; }
else
  die "neither sha256sum nor shasum found; cannot verify the download."
fi

TMP="$(mktemp -d "${TMPDIR:-/tmp}/page-install.XXXXXX")" || die "could not create a temp directory."
trap 'rm -rf "$TMP"' EXIT INT TERM
umask 077

printf 'Fetching manifest…\n'
curl -fsSL --proto '=https' --tlsv1.2 "$BASE/manifest.json" -o "$TMP/manifest.json" \
  || die "could not fetch the manifest from $BASE."

# node is already a hard dependency, so parse the manifest with it rather than
# grepping JSON.
expected_cli="$(node -e 'const m=require(process.argv[1]);process.stdout.write(String((m.files&&m.files["cli.js"]&&m.files["cli.js"].sha256)||""))' "$TMP/manifest.json")" \
  || die "malformed manifest."
expected_mcp="$(node -e 'const m=require(process.argv[1]);process.stdout.write(String((m.files&&m.files["mcp.js"]&&m.files["mcp.js"].sha256)||""))' "$TMP/manifest.json")" \
  || die "malformed manifest."
version="$(node -e 'const m=require(process.argv[1]);process.stdout.write(String(m.version||"unknown"))' "$TMP/manifest.json")"

[ -n "$expected_cli" ] || die "manifest is missing a checksum for cli.js."
[ -n "$expected_mcp" ] || die "manifest is missing a checksum for mcp.js."

fetch_verify() {
  remote="$1"; local_name="$2"; expected="$3"
  printf 'Fetching %s…\n' "$remote"
  curl -fsSL --proto '=https' --tlsv1.2 "$BASE/$remote" -o "$TMP/$local_name" \
    || die "could not download $remote."
  [ -s "$TMP/$local_name" ] || die "$remote downloaded empty."
  actual="$(sha256 "$TMP/$local_name")"
  if [ "$actual" != "$expected" ]; then
    printf 'expected %s\nactual   %s\n' "$expected" "$actual" >&2
    die "checksum mismatch for $remote — refusing to install."
  fi
  node --check "$TMP/$local_name" >/dev/null 2>&1 \
    || die "$remote is not valid JavaScript — refusing to install."
  printf '  verified %s\n' "$actual"
}

fetch_verify "cli.js" "page.js" "$expected_cli"
fetch_verify "mcp.js" "mcp.js" "$expected_mcp"

mkdir -p "$BIN_DIR" "$LIB_DIR" || die "could not create $BIN_DIR or $LIB_DIR."

# Everything verified; now swap it in.
mv "$TMP/page.js" "$LIB_DIR/page.js"
mv "$TMP/mcp.js" "$LIB_DIR/mcp.js"
chmod 644 "$LIB_DIR/page.js" "$LIB_DIR/mcp.js"

# Bake in the absolute node path. A bare `exec node` only works when the
# caller has a PATH that includes it, which is false for cron, systemd, and
# non-interactive SSH — exactly the unattended contexts a page matters most
# in. It failed silently there before. Fall back to a PATH lookup so an nvm
# version bump that removes this exact binary degrades instead of breaking.
NODE_BIN="$(command -v node 2>/dev/null)"
[ -n "$NODE_BIN" ] || die "node not found on PATH."
cat > "$TMP/page" <<LAUNCHER
#!/bin/sh
NODE="$NODE_BIN"
[ -x "\$NODE" ] || NODE="\$(command -v node 2>/dev/null)"
[ -n "\$NODE" ] || { echo "page: node not found (looked for $NODE_BIN, then PATH)" >&2; exit 127; }
exec "\$NODE" "$LIB_DIR/page.js" "\$@"
LAUNCHER
chmod 755 "$TMP/page"
mv "$TMP/page" "$BIN_DIR/page"

cat <<EOF

Installed Page $version
  page CLI    $BIN_DIR/page
  MCP server  $LIB_DIR/mcp.js

SHA-256, also published at github.com/joey-io/pager/blob/main/CHECKSUMS.txt
  cli.js  $expected_cli
  mcp.js  $expected_mcp

Next:
  page login
  claude mcp add page -- node $LIB_DIR/mcp.js
EOF

case ":${PATH}:" in
  *":$BIN_DIR:"*) ;;
  *) printf '\nNote: add %s to your PATH.\n' "$BIN_DIR" ;;
esac
