#!/usr/bin/env bash
# BSM Client installer — served by BSM Cloud (https://bsm-claude.bs-soft.co.kr)
#   curl -fsSL https://bsm-claude.bs-soft.co.kr/install.sh | BSM_ENROLLMENT_KEY=... bash
# Options (env):
#   BSM_CLOUD_URL       cloud base URL           (default: https://bsm-claude.bs-soft.co.kr)
#   BSM_HOME            install root             (default: $HOME/.bsm)
#   BSM_VERSION         client version           (default: latest from /versions)
#   BSM_PORT            web UI port              (default: 26001)
#   BSM_DEVICE_NAME     pre-set device name      (optional; else set in web setup)
#   BSM_ENROLLMENT_KEY  cloud enrollment key     (optional; else set in web setup)
#   BSM_PUBLIC_URL      public URL of this client's web UI (optional)
#   BSM_NO_SYSTEMD=1    skip systemd unit
set -euo pipefail

CLOUD="${BSM_CLOUD_URL:-https://bsm-claude.bs-soft.co.kr}"
BSM_HOME="${BSM_HOME:-$HOME/.bsm}"
PORT="${BSM_PORT:-26001}"
NODE_MIN=22

say()  { printf '\033[1;36m[bsm]\033[0m %s\n' "$*"; }
die()  { printf '\033[1;31m[bsm] ERROR:\033[0m %s\n' "$*" >&2; exit 1; }
need() { command -v "$1" >/dev/null 2>&1 || die "'$1' is required"; }
need curl; need tar; need sha256sum

mkdir -p "$BSM_HOME"/{releases,data,tmp}
export BSM_HOME

# ---- 1. Node.js ---------------------------------------------------------
NODE_BIN=""
if command -v node >/dev/null 2>&1; then
  v=$(node -v | sed 's/^v//' | cut -d. -f1)
  if [ "$v" -ge "$NODE_MIN" ]; then NODE_BIN="$(command -v node)"; fi
fi
if [ -x "$BSM_HOME/node/bin/node" ]; then NODE_BIN="$BSM_HOME/node/bin/node"; fi
if [ -z "$NODE_BIN" ]; then
  say "Node.js >= $NODE_MIN not found; downloading a private copy into $BSM_HOME/node"
  arch=$(uname -m); case "$arch" in x86_64) narch=x64;; aarch64|arm64) narch=arm64;; *) die "unsupported arch $arch";; esac
  NODE_VER="${BSM_NODE_VERSION:-v22.20.0}"
  curl -fsSL "https://nodejs.org/dist/${NODE_VER}/node-${NODE_VER}-linux-${narch}.tar.xz" -o "$BSM_HOME/tmp/node.tar.xz"
  rm -rf "$BSM_HOME/node"; mkdir -p "$BSM_HOME/node"
  tar -xJf "$BSM_HOME/tmp/node.tar.xz" -C "$BSM_HOME/node" --strip-components=1
  NODE_BIN="$BSM_HOME/node/bin/node"
fi
say "using node: $NODE_BIN ($("$NODE_BIN" -v))"

# ---- 2. Resolve version + download --------------------------------------
say "querying $CLOUD/versions"
MANIFEST=$("$NODE_BIN" -e '
  const u=process.argv[1]; fetch(u,{signal:AbortSignal.timeout(15000)}).then(r=>r.json()).then(j=>{const c=j["bsm-client"]; if(!c||!c.latest){console.error("no client release published");process.exit(2)}
  console.log([c.latest,c.download,c.sha256,c.signature||"",c.public_key_url||""].join(" "))}).catch(e=>{console.error(e.message);process.exit(1)})' "$CLOUD/versions") || die "cannot read version manifest from $CLOUD"
read -r LATEST DL SHA SIG PUBKEY_URL <<<"$MANIFEST"
VERSION="${BSM_VERSION:-$LATEST}"
if [ "$VERSION" != "$LATEST" ]; then
  DL="$CLOUD/download/bsm-client/$VERSION.tar.gz"
  SHA=$(curl -fsSL "$DL.sha256" | awk '{print $1}')
  SIG=""
fi
TGZ="$BSM_HOME/tmp/bsm-client-$VERSION.tar.gz"
say "downloading bsm-client $VERSION"
curl -fsSL "$DL" -o "$TGZ"

# ---- 3. Verify SHA-256 (+ Ed25519 signature when available) ------------
ACTUAL=$(sha256sum "$TGZ" | awk '{print $1}')
[ "$ACTUAL" = "$SHA" ] || die "SHA-256 mismatch: expected $SHA got $ACTUAL"
say "sha256 OK: $SHA"
PUBKEY_FILE="$BSM_HOME/data/cloud-signing.pub"
if [ -n "$SIG" ]; then
  curl -fsSL "${PUBKEY_URL:-$CLOUD/pubkey}" -o "$PUBKEY_FILE"
  "$NODE_BIN" -e '
    const c=require("crypto"),fs=require("fs");const [pub,hex,sig]=process.argv.slice(1);
    const ok=c.verify(null,Buffer.from(hex),c.createPublicKey(fs.readFileSync(pub)),Buffer.from(sig,"base64"));
    if(!ok){console.error("signature invalid");process.exit(1)}' "$PUBKEY_FILE" "$SHA" "$SIG" || die "release signature verification failed"
  say "ed25519 signature OK"
fi

# ---- 4. Install into versioned dir + 'current' symlink -----------------
DEST="$BSM_HOME/releases/$VERSION"
rm -rf "$DEST"; mkdir -p "$DEST"
tar -xzf "$TGZ" -C "$DEST" --strip-components=1
ln -sfn "$DEST" "$BSM_HOME/current"
ln -sfn "$NODE_BIN" "$BSM_HOME/node-bin"
echo "$VERSION" > "$BSM_HOME/data/installed-version"

# ---- 5. Seed config (only what the installer knows) --------------------
CFG="$BSM_HOME/data/config.json"
if [ ! -f "$CFG" ]; then
  "$NODE_BIN" -e '
    const fs=require("fs");const [file,cloud,port,name,key,pub]=process.argv.slice(1);
    const cfg={cloud_url:cloud,port:Number(port),bind:"0.0.0.0"};
    if(name) cfg.device_name=name; if(key) cfg.enrollment_key=key; if(pub) cfg.public_url=pub;
    fs.writeFileSync(file,JSON.stringify(cfg,null,2));fs.chmodSync(file,0o600);' \
    "$CFG" "$CLOUD" "$PORT" "${BSM_DEVICE_NAME:-}" "${BSM_ENROLLMENT_KEY:-}" "${BSM_PUBLIC_URL:-}"
fi

# ---- 6. systemd (user unit if not root, system unit if root) -----------
if [ "${BSM_NO_SYSTEMD:-0}" != "1" ] && command -v systemctl >/dev/null 2>&1; then
  if [ "$(id -u)" = "0" ]; then
    UNIT=/etc/systemd/system/bsm-client.service; SC="systemctl"
  else
    mkdir -p "$HOME/.config/systemd/user"; UNIT="$HOME/.config/systemd/user/bsm-client.service"; SC="systemctl --user"
  fi
  sed -e "s|__BSM_HOME__|$BSM_HOME|g" "$DEST/bsm-client.service.tpl" > "$UNIT"
  $SC daemon-reload
  $SC enable bsm-client.service >/dev/null 2>&1 || true
  $SC restart bsm-client.service
  if [ "$(id -u)" != "0" ] && command -v loginctl >/dev/null 2>&1; then loginctl enable-linger "$USER" 2>/dev/null || true; fi
  say "systemd unit installed: $UNIT"
else
  say "systemd skipped; start manually: BSM_HOME=$BSM_HOME $NODE_BIN $BSM_HOME/current/bsmd.js"
fi

# ---- 7. Health check ----------------------------------------------------
for i in $(seq 1 20); do
  if curl -fsS "http://127.0.0.1:$PORT/api/health" >/dev/null 2>&1; then
    say "BSM Client $VERSION is running:  http://$(hostname -I 2>/dev/null | awk '{print $1}'):$PORT"
    say "Open the web UI to finish setup (device name, admin password, cloud registration)."
    exit 0
  fi
  sleep 1
done
say "installed, but health check did not pass yet. Check: journalctl --user -u bsm-client -n 50"
exit 0
