#!/usr/bin/env sh
# Install the `pressure` + `pressure-mcp` binaries (PRD-PRES-11 §8 / DEC-PRES-10).
#
#   curl -fsSL https://pressure.dev/install.sh | sh
#
# pressure.dev serves this script from Firebase Hosting. The release bucket supplies
# the binaries and a signed latest manifest; it is not the trust anchor. The public
# key below is byte-identical to keys/pressure-cli-signing.pub.pem.
#
# Env / flags:
#   PRESSURE_INSTALL_DIR=DIR   install dir (default: ~/.local/bin)      | --dir DIR
#   PRESSURE_VERSION=TAG       pin a release tag (default: signed latest)| --version TAG
#   PRESSURE_RELEASES_URL=URL  override the bucket base URL
set -eu

BUCKET_URL="${PRESSURE_RELEASES_URL:-https://storage.googleapis.com/quirence-pressure-cli-releases}"
INSTALL_DIR="${PRESSURE_INSTALL_DIR:-$HOME/.local/bin}"
VERSION="${PRESSURE_VERSION:-}"
RELEASE_PUBKEY='-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAxSZ1KvhJMRHABrPIkBIz4/HklXuv8x5inHJIl5uBqTQ=
-----END PUBLIC KEY-----'

while [ $# -gt 0 ]; do
  case "$1" in
    --dir) INSTALL_DIR="$2"; shift 2 ;;
    --version) VERSION="$2"; shift 2 ;;
    *) echo "unknown argument: $1" >&2; exit 2 ;;
  esac
done

os="$(uname -s)"
arch="$(uname -m)"
case "$os" in
  Darwin) os_tag="darwin" ;;
  Linux)  os_tag="linux" ;;
  *) echo "unsupported OS: $os (only macOS and Linux have binaries; Windows is a TODO)" >&2; exit 1 ;;
esac
case "$arch" in
  arm64|aarch64) arch_tag="arm64" ;;
  x86_64|amd64)  arch_tag="x64" ;;
  *) echo "unsupported architecture: $arch" >&2; exit 1 ;;
esac
suffix="${os_tag}-${arch_tag}"

if [ "$suffix" = "darwin-x64" ]; then
  echo "No prebuilt binary for Intel macOS (darwin-x64) — only Apple Silicon is shipped." >&2
  echo "Install from source instead:  pipx install 'git+https://github.com/quirence-ai/pressure#subdirectory=cli'" >&2
  echo "(or, from a checkout:  pipx install ./cli)" >&2
  exit 1
fi

if command -v sha256sum >/dev/null 2>&1; then
  sha256() { sha256sum "$1" | awk '{print $1}'; }
elif command -v shasum >/dev/null 2>&1; then
  sha256() { shasum -a 256 "$1" | awk '{print $1}'; }
else
  echo "no sha256 tool (sha256sum / shasum) found; cannot verify downloads" >&2
  exit 1
fi

b64decode() {
  if printf '' | base64 -d >/dev/null 2>&1; then
    base64 -d
  else
    base64 -D
  fi
}

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

verify_release_signature() {
  file=$1
  sig_b64=$2
  printf '%s\n' "$RELEASE_PUBKEY" > "$tmp/release.pub.pem"
  b64decode < "$sig_b64" > "$tmp/release.sig" || return 1
  if openssl pkeyutl -verify -pubin -inkey "$tmp/release.pub.pem" \
      -rawin -in "$file" -sigfile "$tmp/release.sig" >/dev/null 2>&1; then
    return 0
  fi
  if ! command -v python3 >/dev/null 2>&1; then
    echo "OpenSSL cannot verify Ed25519 and python3 is not installed." >&2
    return 1
  fi
  python3 - "$file" "$tmp/release.sig" "$tmp/release.pub.pem" <<'PY'
"""Ed25519 verify (RFC 8032). Stdlib only.

Release signatures are raw 64-byte Ed25519. macOS LibreSSL cannot load an
Ed25519 SPKI key, so both `pressure update` and install-pressure.sh use this
verifier. The shell script inlines a copy so `curl | sh` does not fetch it.
"""

import hashlib

P = 2**255 - 19
L = 2**252 + 27742317777372353535851937790883648493
D = (-121665 * pow(121666, P - 2, P)) % P
I = pow(2, (P - 1) // 4, P)
BY = (4 * pow(5, P - 2, P)) % P


def _sha512(data: bytes) -> bytes:
    return hashlib.sha512(data).digest()


def _inv(x: int) -> int:
    return pow(x, P - 2, P)


def _xrecover(y: int) -> int:
    xx = (y * y - 1) * _inv(D * y * y + 1) % P
    x = pow(xx, (P + 3) // 8, P)
    if (x * x - xx) % P != 0:
        x = (x * I) % P
    if (x * x - xx) % P != 0:
        raise ValueError("not on curve")
    if x % 2 != 0:
        x = P - x
    return x


def _isoncurve(point):
    x, y = point
    return (-x * x + y * y - 1 - D * x * x * y * y) % P == 0


BX = _xrecover(BY)
B = (BX, BY)


def _edwards_add(p, q):
    x1, y1 = p
    x2, y2 = q
    x3 = (x1 * y2 + x2 * y1) * _inv(1 + D * x1 * x2 * y1 * y2)
    y3 = (y1 * y2 + x1 * x2) * _inv(1 - D * x1 * x2 * y1 * y2)
    return (x3 % P, y3 % P)


def _scalarmult(point, e):
    result = (0, 1)
    addend = point
    while e:
        if e & 1:
            result = _edwards_add(result, addend)
        addend = _edwards_add(addend, addend)
        e >>= 1
    return result


def _decodepoint(s):
    if len(s) != 32:
        raise ValueError("bad point length")
    y = int.from_bytes(s, "little")
    sign = (y >> 255) & 1
    y &= (1 << 255) - 1
    x = _xrecover(y)
    if (x & 1) != sign:
        x = P - x
    point = (x, y)
    if not _isoncurve(point):
        raise ValueError("point off curve")
    return point


def _decodeint(s: bytes) -> int:
    return int.from_bytes(s, "little")


def verify(public: bytes, signature: bytes, message: bytes) -> bool:
    """Return True when `signature` is a raw 64-byte Ed25519 sig over `message`."""
    try:
        if len(public) != 32 or len(signature) != 64:
            return False
        R = _decodepoint(signature[:32])
        A = _decodepoint(public)
        S = _decodeint(signature[32:])
        if S >= L:
            return False
        digest = _sha512(signature[:32] + public + message)
        k = int.from_bytes(digest, "little") % L
        return _scalarmult(B, S) == _edwards_add(R, _scalarmult(A, k))
    except Exception:
        return False


def raw_public_from_spki_pem(pem: str) -> bytes:
    """Last 32 bytes of an Ed25519 SPKI PEM (the raw public key)."""
    import base64

    body = "".join(
        line.strip()
        for line in pem.splitlines()
        if line.strip() and not line.startswith("-----")
    )
    der = base64.b64decode(body)
    # SPKI: 12-byte AlgorithmIdentifier prefix, then the 32-byte key.
    if len(der) < 32:
        raise ValueError("SPKI too short")
    return der[-32:]

import sys
pem = open(sys.argv[3], encoding="utf8").read()
signature = open(sys.argv[2], "rb").read()
message = open(sys.argv[1], "rb").read()
raise SystemExit(0 if verify(raw_public_from_spki_pem(pem), signature, message) else 1)
PY
}

mkdir -p "$INSTALL_DIR"
echo "Installing pressure ($suffix) to $INSTALL_DIR ..."

if [ -z "$VERSION" ]; then
  echo "  resolving latest ..."
  curl -fsSL "$BUCKET_URL/latest.manifest" -o "$tmp/latest.manifest" \
    || { echo "could not download latest.manifest" >&2; exit 1; }
  curl -fsSL "$BUCKET_URL/latest.manifest.sig" -o "$tmp/latest.manifest.sig" \
    || { echo "could not download latest.manifest.sig" >&2; exit 1; }
  verify_release_signature "$tmp/latest.manifest" "$tmp/latest.manifest.sig" \
    || { echo "signature verification failed for latest.manifest" >&2; exit 1; }
  VERSION=$(awk '$1 == "version" { print $2 }' "$tmp/latest.manifest")
  if [ -z "$VERSION" ]; then
    echo "latest.manifest has no version" >&2
    exit 1
  fi
fi

echo "Installing pressure ($suffix, $VERSION) ..."

for bin in pressure pressure-mcp; do
  asset="${bin}-${suffix}"
  url="$BUCKET_URL/$VERSION/$asset"
  echo "  downloading $asset ..."
  if ! curl -fSL "$url" -o "$tmp/$bin"; then
    echo "failed to download $url" >&2
    exit 1
  fi
  want="$(curl -fsSL "${url}.sha256" 2>/dev/null | awk '{print $1}' || true)"
  if [ -z "$want" ]; then
    echo "missing checksum for $asset" >&2
    exit 1
  fi
  got="$(sha256 "$tmp/$bin")"
  if [ "$want" != "$got" ]; then
    echo "checksum mismatch for $asset: expected $want, got $got" >&2
    exit 1
  fi
  if [ -f "$tmp/latest.manifest" ]; then
    signed=$(awk -v name="$asset" '$1 == name { print $2 }' "$tmp/latest.manifest")
    if [ "$signed" != "$got" ]; then
      echo "signed manifest hash does not match $asset" >&2
      exit 1
    fi
  fi
  curl -fsSL "${url}.sig" -o "$tmp/$bin.sig" || { echo "missing signature for $asset" >&2; exit 1; }
  verify_release_signature "$tmp/$bin" "$tmp/$bin.sig" \
    || { echo "signature verification failed for $asset" >&2; exit 1; }
  chmod +x "$tmp/$bin"
  mv "$tmp/$bin" "$INSTALL_DIR/$bin"
done

if [ "$os_tag" = "darwin" ]; then
  xattr -d com.apple.quarantine "$INSTALL_DIR/pressure" "$INSTALL_DIR/pressure-mcp" 2>/dev/null || true
fi

echo
if ! "$INSTALL_DIR/pressure" --version; then
  echo "installed, but '$INSTALL_DIR/pressure --version' failed to run" >&2
  exit 1
fi

if ! "$INSTALL_DIR/pressure" install-skills --sync; then
  echo "skill sync failed; run '$INSTALL_DIR/pressure install-skills --sync' manually" >&2
fi

case ":$PATH:" in
  *":$INSTALL_DIR:"*) ;;
  *) echo "
NOTE: $INSTALL_DIR is not on your PATH. Add it, e.g.:
  echo 'export PATH=\"$INSTALL_DIR:\$PATH\"' >> ~/.zshrc" ;;
esac

echo "
Done. Next:
  pressure login              # authorize this machine in the browser
  pressure install-skills     # install the Pressure skills into ~/.claude/skills

Later, upgrade in place with:
  pressure update             # replaces the binaries AND refreshes installed skills (pressure update --check to peek)"
