#!/bin/bash
#
# Volatclaw server installer.
#
# Server bootstrap flow (run on a fresh Ubuntu box):
#
# 1. As root, create a non-root user with passwordless sudo:
#      useradd -m -s /bin/bash volatclaw
#      echo 'volatclaw ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/volatclaw
#      chmod 440 /etc/sudoers.d/volatclaw
#      su - volatclaw
#
# 2. As volatclaw, add your laptop's pubkey for direct SSH (optional):
#      mkdir -p ~/.ssh && chmod 700 ~/.ssh
#      vi ~/.ssh/authorized_keys      # paste laptop's ~/.ssh/id_rsa.pub
#      chmod 600 ~/.ssh/authorized_keys
#
# 3. Run the installer:
#      curl -fsSL https://install.volat.ai | bash
#
#    The script prompts interactively for the LICENSE_KEY, the RW PAT (bot repos),
#    and DOMAIN. To skip prompts, pass them as env vars:
#      curl -fsSL https://install.volat.ai | \
#        LICENSE_KEY=lic_xxx GITHUB_PAT_RW=ghp_yyy \
#        DOMAIN=conductor.example.com bash
#
#    DOMAIN is optional — Enter (or omit) for polling mode.
#
set -e

echo "=== Volatclaw Server Setup ==="

# The compiled build is fetched from the license server (license.volat.ai), which
# returns a short-lived signed Supabase download URL for a valid LICENSE_KEY. No
# source/dist repo access is needed on the customer box — only a license key.
LICENSE_SERVER="${LICENSE_SERVER:-https://license.volat.ai}"

# Refuse root — Claude Code refuses to run as root, which makes the agent
# subprocess exit 1. Run as a regular user with sudo privileges instead.
if [ "$(id -u)" = "0" ]; then
  cat >&2 <<'ROOTERR'
ERROR: Don't run setup.sh as root.

Claude Code (the agent SDK subprocess) refuses to run as root, so the
conductor would start but every bot reply would fail.

Create a non-root user and run the installer as them:

  useradd -m -s /bin/bash volatclaw
  usermod -aG sudo volatclaw
  su - volatclaw

  # Then run the curl|bash one-liner with your env vars
ROOTERR
  exit 1
fi

# Require passwordless sudo — curl|bash has no TTY, so a sudo prompt mid-script
# silently hangs forever.
if ! sudo -n true 2>/dev/null; then
  cat >&2 <<'NOSUDO'
ERROR: This installer needs passwordless sudo.

It runs apt-get, npm i -g, and writes the systemd unit. Piped to bash,
there's no TTY for a password prompt — sudo would hang silently.

As root:
  echo 'volatclaw ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/volatclaw
  chmod 440 /etc/sudoers.d/volatclaw

Then re-run the installer as the volatclaw user.
NOSUDO
  exit 1
fi

# Collect inputs — env vars take precedence; otherwise prompt via /dev/tty
# (works under curl|bash, where stdin is the pipe but the controlling TTY is still around).
prompt_secret() { # var-name, prompt-text — reads char-by-char so we can echo *
  local val='' char
  if [ ! -e /dev/tty ]; then
    echo "ERROR: $1 not set and no TTY available for prompt." >&2
    exit 1
  fi
  printf '%s: ' "$2" > /dev/tty
  while IFS= read -rsn1 char < /dev/tty; do
    if [ -z "$char" ]; then
      break  # Enter
    elif [ "$char" = $'\x7f' ] || [ "$char" = $'\b' ]; then
      if [ -n "$val" ]; then
        val=${val%?}
        printf '\b \b' > /dev/tty
      fi
    else
      val+=$char
      printf '*' > /dev/tty
    fi
  done
  printf '\n' > /dev/tty
  printf -v "$1" '%s' "$val"
}
prompt_visible() { # var-name, prompt-text
  local val
  [ -e /dev/tty ] || return 0  # skip silently if no TTY (e.g., DOMAIN is optional)
  read -rp "$2: " val < /dev/tty
  printf -v "$1" '%s' "$val"
}

if [ -z "${LICENSE_KEY:-}" ]; then
  echo
  echo "Volatclaw license key (lic_...) — issued from $LICENSE_SERVER"
  prompt_secret LICENSE_KEY "  LICENSE_KEY"
fi
if [ -z "${GITHUB_PAT_RW:-}" ]; then
  echo
  echo "GitHub fine-grained PAT — Contents:Read+Write on bot repos"
  prompt_secret GITHUB_PAT_RW "  RW PAT"
fi
if [ -z "${DOMAIN:-}" ]; then
  echo
  echo "Webhook domain (e.g. conductor.customer.com) — empty for polling mode"
  prompt_visible DOMAIN "  DOMAIN"
fi

# STT provider — pick one and collect the matching API key. Both providers use
# Whisper-family models with the same OpenAI-compatible API; Groq has a free
# tier and is faster, OpenAI is paid and the original.
if [ -z "${STT_PROVIDER:-}" ]; then
  echo
  echo "Speech-to-text provider:"
  echo "  1) openai  — Whisper-1 (paid)"
  echo "  2) groq    — whisper-large-v3-turbo (free tier, fast)"
  prompt_visible STT_CHOICE "  Choice [1-2, default 1]"
  case "$STT_CHOICE" in
    2|groq) STT_PROVIDER=groq ;;
    *) STT_PROVIDER=openai ;;
  esac
fi
case "$STT_PROVIDER" in
  groq)
    if [ -z "${GROQ_API_KEY:-}" ]; then
      echo
      echo "Groq API key (free at console.groq.com)"
      prompt_secret GROQ_API_KEY "  GROQ_API_KEY"
    fi
    [ -n "$GROQ_API_KEY" ] || { echo "ERROR: GROQ_API_KEY is required for STT_PROVIDER=groq" >&2; exit 1; }
    ;;
  openai)
    if [ -z "${OPENAI_API_KEY:-}" ]; then
      echo
      echo "OpenAI API key"
      prompt_secret OPENAI_API_KEY "  OPENAI_API_KEY"
    fi
    [ -n "$OPENAI_API_KEY" ] || { echo "ERROR: OPENAI_API_KEY is required for STT_PROVIDER=openai" >&2; exit 1; }
    ;;
  *)
    echo "ERROR: unknown STT_PROVIDER='$STT_PROVIDER' (expected: openai, groq)" >&2
    exit 1
    ;;
esac

[ -n "$LICENSE_KEY" ] || { echo "ERROR: LICENSE_KEY is required" >&2; exit 1; }
[ -n "$GITHUB_PAT_RW" ] || { echo "ERROR: GITHUB_PAT_RW is required (for cloning bot repos)" >&2; exit 1; }

# Verify the license key resolves to a downloadable build before we touch disk.
echo "Verifying license key..."
art_status=$(curl -s -o /dev/null -w '%{http_code}' -X POST "$LICENSE_SERVER/artifact" \
  -H 'content-type: application/json' -d "{\"licenseKey\":\"$LICENSE_KEY\"}")
if [ "$art_status" != "200" ]; then
  echo "ERROR: license key not accepted by $LICENSE_SERVER (HTTP $art_status)." >&2
  echo "       Check the key is correct and hasn't been revoked." >&2
  exit 1
fi
echo "  license key valid ✓"

# DOMAIN is optional — when set, configures webhook mode. Otherwise polling.
if [ -n "${DOMAIN:-}" ]; then
  echo "Webhook mode requested for domain: $DOMAIN"
  domain_ip=$(getent ahostsv4 "$DOMAIN" 2>/dev/null | awk 'NR==1{print $1}' || echo "")
  if [ -z "$domain_ip" ]; then
    echo "ERROR: $DOMAIN does not resolve. Check DNS." >&2
    exit 1
  fi
  echo "  $DOMAIN → $domain_ip"
else
  echo
  echo "  → polling mode (no DOMAIN set)"
fi

# Install git if missing
if ! command -v git &> /dev/null; then
  echo "Installing git..."
  sudo apt-get update
  sudo apt-get install -y git
fi

# Configure git credentials: the RW PAT for github.com (host-only). This is used
# only for cloning the customer's BOT repos — the volatclaw build itself comes
# from the license server, not git.
echo "Configuring git credentials..."
git config --global credential.helper store
umask 077
cat > ~/.git-credentials <<EOF
https://atanych:${GITHUB_PAT_RW}@github.com
EOF
chmod 600 ~/.git-credentials

# Install the EXACT nodejs.org official Node — NOT nodesource. The shipped artifact
# is V8 bytecode compiled in CI; V8 cached data is only loadable on the identical
# Node *build*, and CI uses the nodejs.org official build (actions/setup-node). A
# nodesource build of the same version produces incompatible bytecode. This MUST
# equal the version pinned in .github/workflows/build-dist.yml.
NODE_VERSION="${NODE_VERSION:-22.22.2}"
if [ "$(/usr/local/bin/node -v 2>/dev/null)" != "v$NODE_VERSION" ]; then
  echo "Installing Node.js $NODE_VERSION (nodejs.org official)..."
  curl -fsSL "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" -o /tmp/node.tar.xz
  sudo tar -xJf /tmp/node.tar.xz -C /usr/local --strip-components=1
  rm -f /tmp/node.tar.xz
fi
export PATH="/usr/local/bin:$PATH"   # ensure the official node/npm win for the rest of this script
echo "Node.js $(/usr/local/bin/node -v) (nodejs.org official, /usr/local/bin)"

# Fetch the compiled build from the license server (Supabase-backed). The license
# key authorizes a short-lived signed download URL; the tarball is the source-free
# bytecode artifact + runtime files. node is present (installed above) to parse
# the JSON response.
echo "Fetching volatclaw build..."
mkdir -p ~/volatclaw
build_url=$(curl -fsS -X POST "$LICENSE_SERVER/artifact" \
  -H 'content-type: application/json' -d "{\"licenseKey\":\"$LICENSE_KEY\"}" \
  | node -e "let s='';process.stdin.on('data',d=>s+=d).on('end',()=>{const j=JSON.parse(s);if(!j.ok){console.error(j.error||'fetch failed');process.exit(1)}process.stdout.write(j.url)})")
[ -n "$build_url" ] || { echo "ERROR: could not obtain artifact URL from $LICENSE_SERVER" >&2; exit 1; }
curl -fsSL "$build_url" -o /tmp/volatclaw.tar.gz
tar -xzf /tmp/volatclaw.tar.gz -C ~/volatclaw
rm -f /tmp/volatclaw.tar.gz

cd ~/volatclaw

# Install dependencies. The artifact ships precompiled bytecode (dist/volatclaw.jsc)
# — no build/compile step here; nothing readable is on disk.
echo "Installing dependencies..."
npm install

# Install agent-browser (needs sudo for global install into /usr/lib/node_modules)
echo "Installing agent-browser..."
sudo npm i -g agent-browser
agent-browser install

# Install stealth-browser CLI — same shape (global `stealth-browser` on PATH).
# `sudo npm link` links the in-tree bin (./bin/stealth-browser.mjs) into the
# global prefix; using sudo matches how agent-browser was installed above and
# avoids permission errors on the system prefix.
echo "Linking stealth-browser..."
sudo npm link
# Install real Chrome for Patchright (best stealth — Patchright's bundled
# chromium has detectable fingerprints).
npx patchright install chrome

# Create bots and tmp directories
mkdir -p bots tmp

# Set up .env if not exists
if [ ! -f .env ]; then
  cp .env.example .env
  echo ""
  echo ">>> Edit ~/volatclaw/.env with your API keys"
fi

# Persist the license key into .env (update.sh reads it to fetch future builds).
if grep -qE '^LICENSE_KEY=' .env; then
  sed -i.bak "s|^LICENSE_KEY=.*|LICENSE_KEY=$LICENSE_KEY|" .env
else
  echo "LICENSE_KEY=$LICENSE_KEY" >> .env
fi
rm -f .env.bak

# Persist STT provider + the matching API key into .env. sed assumes the keys
# already exist in .env (they're in .env.example) — re-runs are safe.
sed -i.bak "s|^STT_PROVIDER=.*|STT_PROVIDER=$STT_PROVIDER|" .env
case "$STT_PROVIDER" in
  groq)   sed -i.bak "s|^GROQ_API_KEY=.*|GROQ_API_KEY=$GROQ_API_KEY|" .env ;;
  openai) sed -i.bak "s|^OPENAI_API_KEY=.*|OPENAI_API_KEY=$OPENAI_API_KEY|" .env ;;
esac
rm -f .env.bak

# Apply webhook config when DOMAIN is set (idempotent — safe on re-runs).
if [ -n "${DOMAIN:-}" ]; then
  sed -i.bak \
    -e "s|^MODE=.*|MODE=webhook|" \
    -e "s|^WEBHOOK_BASE_URL=.*|WEBHOOK_BASE_URL=https://$DOMAIN|" \
    -e "s|^PORT=.*|PORT=80|" \
    .env
  rm -f .env.bak
  echo "Configured .env for webhook mode at https://$DOMAIN (PORT=80)"
fi

# Generate METRICS_PASSWORD if blank — /metrics is gated on it, so an empty
# value would otherwise leave the endpoint wide open on a public server.
if grep -qE '^METRICS_PASSWORD=$' .env; then
  metrics_pw=$(openssl rand -hex 16)
  sed -i.bak "s|^METRICS_PASSWORD=$|METRICS_PASSWORD=$metrics_pw|" .env
  rm -f .env.bak
  echo "Generated METRICS_PASSWORD: $metrics_pw  (saved to ~/volatclaw/.env)"
fi

# Install systemd service. The unit-file heredoc lives in install-volatclaw-unit.sh
# so the same content is used here and on standalone runs against existing servers.
echo "Installing systemd service..."
"$HOME/volatclaw/scripts/install-volatclaw-unit.sh"

# Restart trigger: a path unit watches $HOME/volatclaw/bots/.restart-request.json.
# The /restart command writes that file; systemd then runs `systemctl restart
# volatclaw` (oneshot) so the bot doesn't have to kill its own process mid-reply.
# The unit-file heredoc lives in install-restart-unit.sh so the same content is
# used here and on standalone runs against existing servers.
"$HOME/volatclaw/scripts/install-restart-unit.sh"

# Schedule daily auto-push of bot repos at 02:00. Idempotent: any existing
# crontab line that mentions push-bots.sh is removed before we add ours.
echo "Scheduling daily push-bots cron at 02:00..."
push_cron="0 2 * * * $HOME/volatclaw/scripts/push-bots.sh >> $HOME/volatclaw/tmp/push-bots.log 2>&1"
( crontab -l 2>/dev/null | grep -v 'volatclaw/scripts/push-bots.sh'; echo "$push_cron" ) | crontab -

echo ""
echo "=== Setup complete ==="
echo "1. Edit ~/volatclaw/.env (API keys)"
echo "2. Clone bot repos via HTTPS: git clone https://github.com/atanych/<bot>.git ~/volatclaw/bots/<bot>"
echo "3. sudo systemctl start volatclaw"
if [ -n "${DOMAIN:-}" ]; then
  echo "4. For each bot: scripts/set-webhook.sh <bot> (registers TG webhook + generates secret)"
  echo "5. journalctl -u volatclaw -f (watch logs)"
else
  echo "4. journalctl -u volatclaw -f (watch logs)"
fi
