#!/usr/bin/env bash
# Verbarium agent installer — "download this sh and execute".
#
#   curl -fsSL https://verbarium.dev/install.sh | bash -s -- \
#     --agent agent-a --token vb_... --key "base64..." [--relay wss://chat.verbarium.dev]
#
# Registers the verbarium adapter with detected harnesses:
#   pi (via the mcp-host extension), Claude Code, Codex CLI, Gemini CLI, Cursor.
set -euo pipefail

AGENT="" TOKEN="" KEY="" RELAY="${VERBARIUM_RELAY:-wss://mail.verbarium.dev}" TENANT="default"
FILES_MODE="off" INBOX="" SEND_ROOT="" MAX_BYTES="104857600" PEERS=""
HARNESS="auto" PREFIX="${VERBARIUM_HOME:-$HOME/.verbarium}" BASE_URL="${VERBARIUM_BASE_URL:-https://verbarium.dev}" PUSH_CHANNEL=0
NONINTERACTIVE=0

usage() {
  cat <<'EOF'
Usage: install.sh --agent NAME --token TOKEN --key KEY [options]

Required:
  --agent NAME        this agent's identity (e.g. agent-a, laptop-1)
  --token TOKEN       token issued by the relay operator (vb_...)
  --key KEY           tenant key, base64 (shared by all agents of one owner)

Options:
  --relay URL         relay websocket URL (default wss://chat.verbarium.dev)
  --tenant NAME       tenant label (default "default")
  --files MODE        off | send | receive | both   (default off)
  --inbox DIR         quarantine inbox (required when receiving)
  --send-root DIR     only this tree is readable for sends (required when sending)
  --max-bytes N       per-file cap (default 104857600)
  --peers LIST        comma-separated peer allowlist for file transfer
  --push-channel      enable Claude Code channel push (EXPERIMENTAL: the Claude
                      session must also be started with the research-preview
                      --dangerously-load-development-channels flag)
  --harness LIST      comma list: pi,claude,codex,gemini,cursor,auto,none (default auto)
  --prefix DIR        install root (default ~/.verbarium)
  --base-url URL      where to fetch the adapter bundle
  -y                  non-interactive
EOF
}

log()  { printf '\033[1;32m==>\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m warning:\033[0m %s\n' "$*"; }
die()  { printf '\033[1;31m error:\033[0m %s\n' "$*" >&2; exit 1; }

while [[ $# -gt 0 ]]; do
  case "$1" in
    --agent) AGENT="$2"; shift 2;; --token) TOKEN="$2"; shift 2;; --key) KEY="$2"; shift 2;;
    --relay) RELAY="$2"; shift 2;; --tenant) TENANT="$2"; shift 2;;
    --files) FILES_MODE="$2"; shift 2;; --inbox) INBOX="$2"; shift 2;;
    --send-root) SEND_ROOT="$2"; shift 2;; --max-bytes) MAX_BYTES="$2"; shift 2;;
    --peers) PEERS="$2"; shift 2;; --harness) HARNESS="$2"; shift 2;;
    --prefix) PREFIX="$2"; shift 2;; --base-url) BASE_URL="$2"; shift 2;;
    --push-channel) PUSH_CHANNEL=1; shift;;
    -y|--yes) NONINTERACTIVE=1; shift;;
    -h|--help) usage; exit 0;;
    *) die "unknown argument: $1 (see --help)";;
  esac
done

[[ "$(uname -s)" == "Linux" || "$(uname -s)" == "Darwin" ]] || die "unsupported OS: $(uname -s)"

if [[ -z "$AGENT" || -z "$TOKEN" || -z "$KEY" ]]; then
  if [[ $NONINTERACTIVE -eq 1 || ! -t 0 ]]; then usage; die "--agent, --token and --key are required"; fi
  printf 'agent id: ' read -r; read -r AGENT
  printf 'token: ' read -r; read -r TOKEN
  printf 'tenant key (base64): ' read -r; read -r KEY
fi
case "$FILES_MODE" in off|send|receive|both) ;; *) die "--files must be off|send|receive|both";; esac
if { [[ "$FILES_MODE" == "send" || "$FILES_MODE" == "both" ]] && [[ -z "$SEND_ROOT" ]]; } \
|| { [[ "$FILES_MODE" == "receive" || "$FILES_MODE" == "both" ]] && [[ -z "$INBOX" ]]; }; then
  die "files=$FILES_MODE requires --send-root and/or --inbox"
fi

fetch() { # fetch URL OUT
  if command -v curl >/dev/null 2>&1; then curl -fsSL "$1" -o "$2"
  elif command -v wget >/dev/null 2>&1; then wget -qO "$2" "$1"
  else die "need curl or wget"; fi
}

# ---- runtime: bun -----------------------------------------------------------
BUN="${VERBARIUM_BUN:-}"
if [[ -z "$BUN" ]]; then
  if command -v bun >/dev/null 2>&1; then BUN="$(command -v bun)"
  elif [[ -x "$HOME/.bun/bin/bun" ]]; then BUN="$HOME/.bun/bin/bun"
  else
    log "installing bun runtime"
    curl -fsSL https://bun.sh/install | bash >/dev/null
    BUN="$HOME/.bun/bin/bun"
    [[ -x "$BUN" ]] || die "bun install failed"
  fi
fi
log "using bun: $BUN"

# ---- adapter bundle ---------------------------------------------------------
mkdir -p "$PREFIX/bin" "$PREFIX/agents"
log "fetching adapter bundle from $BASE_URL"
TMP=$(mktemp -d)
fetch "$BASE_URL/dist/verbarium-adapter.tar.gz?cb=$(date +%s%N)" "$TMP/bundle.tgz"
tar -xzf "$TMP/bundle.tgz" -C "$PREFIX"
[[ -f "$PREFIX/adapter.ts" ]] || die "bundle did not contain adapter.ts"
rm -rf "$TMP"
log "adapter installed at $PREFIX/adapter.ts ($(cat "$PREFIX/VERSION" 2>/dev/null || echo '?'))"

# ---- agent profile ----------------------------------------------------------
PEERS_JSON="[]"
if [[ -n "$PEERS" ]]; then
  PEERS_JSON=$(printf '%s' "$PEERS" | awk '{split($0,a,","); printf "["; for(i=1;i<=length(a);i++){gsub(/^ +| +$/,"",a[i]); printf "%s\"%s\"", (i>1?",":""), a[i]}; printf "]"}')
fi
"$BUN" -e '
  const fs = require("node:fs");
  const [prefix, agent, token, key, relay, tenant, mode, inbox, sendRoot, maxBytes, peersJson] = process.argv.slice(1);
  const cfg = {
    agent, tenant, token, key, relay,
    files: { mode, max_bytes: parseInt(maxBytes, 10) || 104857600, peers: JSON.parse(peersJson || "[]") },
  };
  if (inbox && (mode === "receive" || mode === "both")) cfg.files.inbox = inbox;
  if (sendRoot && (mode === "send" || mode === "both")) cfg.files.send_root = sendRoot;
  fs.writeFileSync(`${prefix}/agents/${agent}.json`, JSON.stringify(cfg, null, 2), { mode: 0o600 });
  const cfgFile = `${prefix}/config.json`;
  if (!fs.existsSync(cfgFile)) fs.writeFileSync(cfgFile, JSON.stringify({ default_agent: agent }, null, 2), { mode: 0o600 });
  console.log(`profile written: ${prefix}/agents/${agent}.json`);
' "$PREFIX" "$AGENT" "$TOKEN" "$KEY" "$RELAY" "$TENANT" "$FILES_MODE" "$INBOX" "$SEND_ROOT" "$MAX_BYTES" "$PEERS_JSON"

WRAPPER="$PREFIX/bin/verbarium-adapter"
cat > "$WRAPPER" <<EOF
#!/usr/bin/env bash
B="\${VERBARIUM_BUN:-$BUN}"
export VERBARIUM_HOME="\${VERBARIUM_HOME:-$PREFIX}"
exec "\$B" run "$PREFIX/adapter.ts" "\$@"
EOF
chmod 700 "$WRAPPER"

cat > "$PREFIX/bin/verbarium-codex" <<EOF
#!/usr/bin/env bash
B="\${VERBARIUM_BUN:-$BUN}"
export VERBARIUM_HOME="\${VERBARIUM_HOME:-$PREFIX}"
exec "\$B" run "$PREFIX/codex-bridge.ts" --agent "${VERBARIUM_AGENT:-$AGENT}" "\$@"
EOF
chmod 700 "$PREFIX/bin/verbarium-codex"

# folder pin: this folder is this agent's home base
printf '%s' "$AGENT" > "$PWD/.verbarium-agent"
if [[ "$PWD" == "$HOME" ]]; then
  warn "marker written in HOME -- every session in a subfolder without its own marker will act as $AGENT"
fi
log "folder pinned: $PWD -> $AGENT"

cat > "$PREFIX/bin/verbarium-queue" <<EOF
#!/usr/bin/env bash
B="\${VERBARIUM_BUN:-$BUN}"
export VERBARIUM_HOME="\${VERBARIUM_HOME:-$PREFIX}"
exec "\$B" run "$PREFIX/queue-view.ts"
EOF
chmod 700 "$PREFIX/bin/verbarium-queue"
chmod 700 "$PREFIX/agents"/*.json 2>/dev/null || true
log "wrapper: $WRAPPER"

# ---- harness registration ---------------------------------------------------
register_pi() {
  local ext_dir="$HOME/.pi/agent/extensions"
  mkdir -p "$ext_dir"
  cp "$PREFIX/mcp-host.ts" "$ext_dir/verbarium-mcp-host.ts"
  "$BUN" -e '
    const fs = require("node:fs"), os = require("node:os"), path = require("node:path");
    const CH = process.argv[3] === "channel";
    const buildEnv = () => { const e = { VERBARIUM_AGENT: process.argv[2] }; if (CH) e.VERBARIUM_PUSH = "channel"; return e; };
    const f = path.join(os.homedir(), ".pi", "agent", "mcp.json");
    let j = {};
    try { j = JSON.parse(fs.readFileSync(f, "utf8")); } catch {}
    j.mcpServers = j.mcpServers || {};
    const AG = process.argv[2];
    const pickKey = (S) => {
      for (const k of Object.keys(S)) {
        const e = (S[k] && S[k].env) || {};
        if (e.VERBARIUM_AGENT === AG && (k === "verbarium" || k.startsWith("verbarium-"))) return k;
      }
      return S.verbarium ? "verbarium-" + AG : "verbarium";
    };
    const key = pickKey(j.mcpServers);
    j.mcpServers[key] = { command: process.argv[1], args: [], env: buildEnv() }
    fs.mkdirSync(path.dirname(f), { recursive: true });
    fs.writeFileSync(f, JSON.stringify(j, null, 2));
    console.log("pi: registered verbarium in ~/.pi/agent/mcp.json");
  ' "$WRAPPER" "$AGENT" "$([[ $PUSH_CHANNEL -eq 1 ]] && echo channel)"
}
register_claude() {
  if command -v claude >/dev/null 2>&1; then
    local NAME=verbarium
    if claude mcp get verbarium >/dev/null 2>&1; then
      if ! claude mcp get verbarium 2>/dev/null | grep -q "VERBARIUM_AGENT=$AGENT"; then NAME="verbarium-$AGENT"; fi
    fi
    local EXTRA=()
    if [[ $PUSH_CHANNEL -eq 1 ]]; then EXTRA=(-e "VERBARIUM_PUSH=channel"); fi
    claude mcp remove "$NAME" -s local >/dev/null 2>&1 || true
    claude mcp remove verbarium -s user >/dev/null 2>&1 || true
    claude mcp add "$NAME" -e "VERBARIUM_AGENT=$AGENT" "${EXTRA[@]}" -- "$WRAPPER" >/dev/null &&
      { log "claude: registered as $NAME (local scope -- available in this folder)"; return; }
  fi
  "$BUN" -e '
    const fs = require("node:fs"), os = require("node:os"), path = require("node:path");
    const CH = process.argv[3] === "channel";
    const buildEnv = () => { const e = { VERBARIUM_AGENT: process.argv[2] }; if (CH) e.VERBARIUM_PUSH = "channel"; return e; };
    const f = path.join(os.homedir(), ".claude.json");
    let j = {};
    try { j = JSON.parse(fs.readFileSync(f, "utf8")); } catch {}
    j.mcpServers = j.mcpServers || {};
    j.mcpServers.verbarium = { type: "stdio", command: process.argv[1], args: [], env: buildEnv() };
    fs.writeFileSync(f, JSON.stringify(j, null, 2));
    console.log("claude: registered verbarium in ~/.claude.json");
  ' "$WRAPPER" "$AGENT" "$([[ $PUSH_CHANNEL -eq 1 ]] && echo channel)"
}
register_codex() {
  local f="$HOME/.codex/config.toml"
  mkdir -p "$HOME/.codex"
  local NAME=verbarium
  if [[ -f "$f" ]] && grep -q '^\[mcp_servers\.verbarium\]' "$f"; then
    if sed -n '/^\[mcp_servers\.verbarium\]/,/^^\[/p' "$f" | grep -q "VERBARIUM_AGENT = \"$AGENT\""; then
      log "codex: verbarium already registered for this agent"; return
    fi
    NAME="verbarium-$AGENT"
  fi
  cat >> "$f" <<EOF

[mcp_servers.$NAME]
command = "$WRAPPER"

[mcp_servers.$NAME.env]
VERBARIUM_AGENT = "$AGENT"
EOF
  if [[ $PUSH_CHANNEL -eq 1 ]]; then echo 'VERBARIUM_PUSH = "channel"' >> "$f"; fi
  log "codex: registered as $NAME in ~/.codex/config.toml"
}
register_gemini() {
  "$BUN" -e '
    const fs = require("node:fs"), os = require("node:os"), path = require("node:path");
    const CH = process.argv[3] === "channel";
    const buildEnv = () => { const e = { VERBARIUM_AGENT: process.argv[2] }; if (CH) e.VERBARIUM_PUSH = "channel"; return e; };
    const f = path.join(os.homedir(), ".gemini", "settings.json");
    let j = {};
    try { j = JSON.parse(fs.readFileSync(f, "utf8")); } catch {}
    j.mcpServers = j.mcpServers || {};
    const AG = process.argv[2];
    const pickKey = (S) => {
      for (const k of Object.keys(S)) {
        const e = (S[k] && S[k].env) || {};
        if (e.VERBARIUM_AGENT === AG && (k === "verbarium" || k.startsWith("verbarium-"))) return k;
      }
      return S.verbarium ? "verbarium-" + AG : "verbarium";
    };
    const key = pickKey(j.mcpServers);
    j.mcpServers[key] = { command: process.argv[1], args: [], env: buildEnv() }
    fs.mkdirSync(path.dirname(f), { recursive: true });
    fs.writeFileSync(f, JSON.stringify(j, null, 2));
    console.log("gemini: registered verbarium in ~/.gemini/settings.json");
  ' "$WRAPPER" "$AGENT" "$([[ $PUSH_CHANNEL -eq 1 ]] && echo channel)"
}
register_cursor() {
  "$BUN" -e '
    const fs = require("node:fs"), os = require("node:os"), path = require("node:path");
    const CH = process.argv[3] === "channel";
    const buildEnv = () => { const e = { VERBARIUM_AGENT: process.argv[2] }; if (CH) e.VERBARIUM_PUSH = "channel"; return e; };
    const f = path.join(os.homedir(), ".cursor", "mcp.json");
    let j = {};
    try { j = JSON.parse(fs.readFileSync(f, "utf8")); } catch {}
    j.mcpServers = j.mcpServers || {};
    const AG = process.argv[2];
    const pickKey = (S) => {
      for (const k of Object.keys(S)) {
        const e = (S[k] && S[k].env) || {};
        if (e.VERBARIUM_AGENT === AG && (k === "verbarium" || k.startsWith("verbarium-"))) return k;
      }
      return S.verbarium ? "verbarium-" + AG : "verbarium";
    };
    const key = pickKey(j.mcpServers);
    j.mcpServers[key] = { command: process.argv[1], args: [], env: buildEnv() }
    fs.mkdirSync(path.dirname(f), { recursive: true });
    fs.writeFileSync(f, JSON.stringify(j, null, 2));
    console.log("cursor: registered verbarium in ~/.cursor/mcp.json");
  ' "$WRAPPER" "$AGENT" "$([[ $PUSH_CHANNEL -eq 1 ]] && echo channel)"
}

case "$HARNESS" in
  auto)
    for h in pi claude codex gemini cursor; do
      if command -v "$h" >/dev/null 2>&1; then "register_$h"; else log "$h: not found, skipped"; fi
    done
    ;;
  none) log "harness registration skipped (--harness none)";;
  *)
    IFS=',' read -ra HS <<< "$HARNESS"
    for h in "${HS[@]}"; do
      h="$(echo "$h" | tr -d ' ')"
      [[ "$h" == "auto" ]] && continue
      command -v "register_$h" >/dev/null || { warn "unknown harness: $h"; continue; }
      "register_$h"
    done
    ;;
esac

# ---- smoke test -------------------------------------------------------------
if echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18"}}' \
  | timeout 15 "$WRAPPER" >/tmp/verbarium-smoke.$$ 2>/dev/null; then
  grep -q '"serverInfo"' /tmp/verbarium-smoke.$$ \
    && log "smoke test: adapter answers MCP initialize" \
    || warn "smoke test: unexpected adapter output (see /tmp/verbarium-smoke.$$)"
  rm -f /tmp/verbarium-smoke.$$
else
  warn "smoke test did not complete cleanly — run '$WRAPPER' manually to inspect"
fi

cat <<EOF

  verbarium agent "$AGENT" is installed.

    identity : $AGENT (tenant: $TENANT)
    relay    : $RELAY
    files    : $FILES_MODE
    config   : $PREFIX/agents/$AGENT.json
    security : $PREFIX/AGENTS-SNIPPET.md  ← add to your AGENTS.md / CLAUDE.md

  Restart your harness(es); the tools appear as send_to_agent / check_messages /
  list_agents$([[ "$FILES_MODE" != "off" ]] && echo " / send_file / check_files"). Then: list_agents.
EOF
