#!/usr/bin/env bash
# CloudWeaver installer — Linux · macOS · WSL · Git Bash
#
# Single entry point for `curl -fsSL <origem>/install.sh | bash` (ADR-0005):
# installs the prerequisites it needs, then installs CloudWeaver into the
# current project folder (ADR-0006). There is deliberately no second copy of
# this script under frontend/public: the Go mux serves "GET /install.sh" from
# this embedded file, which shadows anything in frontend/dist, so a copy there
# would only ever run under Vite in dev — a different install than production.
set -euo pipefail

PLUGIN="fagnerlopes/cloud-weaver"
AGENTS=(universal claude-code codex opencode hermes-agent)

say()  { printf "\033[1;36m[say]\033[0m %s\n" "$*"; }
ok()   { printf "\033[1;32m[ok]\033[0m %s\n" "$*"; }
warn() { printf "\033[1;33m[!]\033[0m %s\n" "$*"; }
fail() { printf "\033[1;31m[x]\033[0m %s\n" "$*" >&2; }

# CloudWeaver installs per project, not globally: the skills land in this
# folder and a bootstrap block in AGENTS.md tells the agent to load the
# playbook on its first turn. Installing into $HOME would pollute it.
# Checked before anything is installed, so a misfired command costs nothing.
if [ "$PWD" = "$HOME" ]; then
  fail "Você está na sua pasta pessoal ($HOME)."
  fail "O CloudWeaver se instala dentro da pasta de um projeto. Crie uma e entre nela:"
  fail "  mkdir ~/meu-hermes && cd ~/meu-hermes"
  exit 1
fi

detect_os() {
  case "$(uname -s)" in
    Linux*)
      if grep -qi microsoft /proc/version 2>/dev/null; then
        echo "wsl"
      else
        echo "linux"
      fi
      ;;
    Darwin*) echo "macos" ;;
    *)       echo "unknown" ;;
  esac
}

OS="$(detect_os)"
say "Verificando pré-requisitos (plataforma: $OS)…"

# ---------------------------------------------------------------------------
# Node.js 18+ — required for `npx skills`
# ---------------------------------------------------------------------------
install_node() {
  say "Instalando Node.js via nvm…"
  export NVM_DIR="$HOME/.nvm"
  curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
  # shellcheck disable=SC1091
  [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
  nvm install 18
  nvm use 18
}

if command -v node >/dev/null 2>&1; then
  NODE_VER="$(node -e 'process.stdout.write(String(process.versions.node.split(".")[0]))')"
  if [ "$NODE_VER" -ge 18 ] 2>/dev/null; then
    ok "Node.js $(node --version)"
  else
    warn "Node.js $NODE_VER encontrado — precisa da versão 18+. Atualizando via nvm…"
    install_node || { fail "Falha ao instalar o Node.js. Instale manualmente: https://nodejs.org"; exit 1; }
  fi
else
  warn "Node.js não encontrado. Instalando via nvm…"
  install_node || { fail "Falha ao instalar o Node.js. Instale manualmente: https://nodejs.org"; exit 1; }
fi

# Make an nvm-provided node visible to the rest of this script.
export NVM_DIR="$HOME/.nvm"
# shellcheck disable=SC1091
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" || true

if ! command -v npx >/dev/null 2>&1; then
  fail "npx não encontrado mesmo após instalar o Node.js. Reabra o terminal e rode novamente."
  exit 1
fi

# ---------------------------------------------------------------------------
# gh (GitHub CLI) — cloud-weaver-repo-setup creates the repo and its secrets
# ---------------------------------------------------------------------------
if command -v gh >/dev/null 2>&1; then
  ok "gh $(gh --version | head -1)"
else
  warn "gh não encontrado. Instalando…"
  if [ "$OS" = "macos" ]; then
    brew install gh || { fail "Falha ao instalar o gh. Instale manualmente: https://cli.github.com"; exit 1; }
  elif [ "$OS" = "linux" ] || [ "$OS" = "wsl" ]; then
    (
      type -p curl >/dev/null || sudo apt-get install curl -y
      curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
        | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
      echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
        | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null
      sudo apt-get update && sudo apt-get install gh -y
    ) || { fail "Falha ao instalar o gh. Instale manualmente: https://cli.github.com"; exit 1; }
  else
    fail "Instale o gh manualmente: https://cli.github.com"
    exit 1
  fi
  ok "gh instalado"
fi

# ---------------------------------------------------------------------------
# python3 — used by the skills to generate secrets and call the Locaweb Cloud API
# ---------------------------------------------------------------------------
if command -v python3 >/dev/null 2>&1; then
  ok "$(python3 --version)"
else
  warn "python3 não encontrado. Instalando…"
  if [ "$OS" = "macos" ]; then
    brew install python || { fail "Falha ao instalar o Python. Instale manualmente: https://python.org"; exit 1; }
  elif [ "$OS" = "linux" ] || [ "$OS" = "wsl" ]; then
    sudo apt-get install -y python3 || { fail "Falha ao instalar o Python. Instale manualmente: https://python.org"; exit 1; }
  else
    fail "Instale o Python 3 manualmente: https://python.org"
    exit 1
  fi
  ok "python3 instalado"
fi

# ---------------------------------------------------------------------------
# ssh — Kamal talks to the VM over SSH
# ---------------------------------------------------------------------------
if command -v ssh >/dev/null 2>&1; then
  ok "ssh disponível"
else
  warn "ssh não encontrado. Instalando…"
  if [ "$OS" = "linux" ] || [ "$OS" = "wsl" ]; then
    sudo apt-get install -y openssh-client || { fail "Falha ao instalar o ssh."; exit 1; }
    ok "ssh instalado"
  elif [ "$OS" = "macos" ]; then
    warn "O ssh já vem com o macOS — se não foi encontrado, reabra o terminal."
  else
    fail "Instale o OpenSSH manualmente."
    exit 1
  fi
fi

# ---------------------------------------------------------------------------
# Permissoes do agente
# ---------------------------------------------------------------------------
# Pin .claude/settings.json in the participant's project, mirroring cofounder's
# installer. Without a pinned allowlist, the `gh secret set` calls in
# cloud-weaver-repo-setup (and the SSH key rotation in cloud-weaver-offboard)
# are stopped by the permission classifier and provisioning stalls halfway.
# "Bash" with no parentheses is tool-scoped: it covers every Bash call, so the
# command never reaches the classifier. The merge preserves the user's other
# keys and never duplicates an entry, so re-running is safe.
#
# Written unconditionally, unlike cofounder, which first checks that ~/.claude
# exists: participants install CloudWeaver before opening the agent for the
# first time, so that directory may not be there yet and the check would fail
# silently — bringing the block right back.
write_claude_settings() {
  local settings script
  mkdir -p "$PWD/.claude"
  settings="$PWD/.claude/settings.json"
  script=$(mktemp)

  cat > "$script" <<'NODE'
const fs = require('fs');
const p = process.argv[2];
let cur = {};
try { cur = JSON.parse(fs.readFileSync(p, 'utf8')); } catch (e) {}
if (cur === null || typeof cur !== 'object' || Array.isArray(cur)) cur = {};
const allow = new Set((cur.permissions && cur.permissions.allow) || []);
for (const a of ['Bash', 'Read', 'WebFetch']) allow.add(a);
cur.permissions = Object.assign({}, cur.permissions, { allow: [...allow] });
fs.writeFileSync(p, JSON.stringify(cur, null, 2) + '\n');
NODE

  # Non-fatal: without it the install still works, only with permission
  # prompts in the middle of provisioning.
  if node "$script" "$settings"; then
    say "Permissoes do projeto configuradas: .claude/settings.json"
  else
    warn "Nao consegui escrever .claude/settings.json — o agente pode pedir permissao durante o provisionamento."
  fi
  rm -f "$script"
}

write_claude_settings

UPDATING=0
if [ -f skills-lock.json ]; then UPDATING=1; fi

# Writes a marker-delimited block into a bootstrap file, replacing the block
# when it is already there so re-running the installer never duplicates it.
write_block() {
  local file="$1" body="$2" tmp
  tmp=$(mktemp)

  if [ -f "$file" ] && grep -q '<!-- cloud-weaver:begin -->' "$file"; then
    awk -v body="$body" '
      /<!-- cloud-weaver:begin -->/ { print body; skip = 1; next }
      /<!-- cloud-weaver:end -->/   { skip = 0; next }
      !skip
    ' "$file" > "$tmp"
    mv "$tmp" "$file"
    say "Atualizado: $file"
    return
  fi

  if [ -f "$file" ] && [ -s "$file" ]; then
    cat "$file" > "$tmp"
    printf '\n' >> "$tmp"
  fi
  printf '%s\n' "$body" >> "$tmp"
  mv "$tmp" "$file"
  say "Criado: $file"
}

AGENTS_BLOCK="<!-- cloud-weaver:begin -->
You are operating as CloudWeaver. Your FIRST action this session — before responding to or acting on the user's message, even if it is a specific task, question, or feature request — MUST be to use the Skill tool to invoke the \`cloud-weaver-playbook\` skill, then follow it as your operating instructions for the entire session. Do not begin the user's request until the playbook is loaded. (If the cloud-weaver skills are not installed in this project, first install them by running \`npx -y skills add $PLUGIN --agent ${AGENTS[*]} --skill '*' -y\`, then invoke the playbook.)
<!-- cloud-weaver:end -->"

CLAUDE_BLOCK="<!-- cloud-weaver:begin -->
The CloudWeaver operating instructions are maintained in @AGENTS.md — read and follow them.
<!-- cloud-weaver:end -->"

# Written BEFORE the skills are installed, on purpose. The block is
# self-contained — it tells the agent how to install the skills if they are
# missing — so if `npx skills` fails below, `set -e` aborts with the folder
# still usable: the participant opens the agent and it recovers on its own.
# The other way round, a failed install left no AGENTS.md and an agent that
# silently never loaded the playbook.
write_block AGENTS.md "$AGENTS_BLOCK"
write_block CLAUDE.md "$CLAUDE_BLOCK"

skills_failed() {
  fail "A instalação das skills falhou."
  fail "A pasta já tem o AGENTS.md: abra seu agente aqui e peça para instalar as skills."
  exit 1
}

if [ "$UPDATING" -eq 1 ]; then
  say "skills-lock.json encontrado — atualizando CloudWeaver nesta pasta: $PWD"
  npx -y skills update || skills_failed
else
  say "Instalando CloudWeaver ($PLUGIN) nesta pasta: $PWD"
  npx -y skills add "$PLUGIN" --agent "${AGENTS[@]}" --skill '*' -y || skills_failed
fi

if [ "$UPDATING" -eq 1 ]; then
  ok "CloudWeaver atualizado nesta pasta!"
else
  ok "CloudWeaver instalado nesta pasta!"
  say "Próximo passo: abra seu agente de código AQUI e peça, em português:"
  say "  \"Crie uma instância do Hermes Agent no Locaweb Cloud\""
  say "Se preferir o menu de receitas, digite /start-cloud."
fi
say "Guia completo: https://github.com/$PLUGIN"
