#!/usr/bin/env bash
# NEX bootstrap — installs Tailscale, bun, configures env, joins Headscale tailnet,
# verifies access to nex-knowledge MCP. Idempotent.
#
# Served at: https://nex.kiliogene.com/onboard.sh (NEX-ADR-0040).
#
# Interactive (default):
#   curl -fsSL https://nex.kiliogene.com/onboard.sh | bash
#
# Non-interactive (CI, automation):
#   NEX_AUTHKEY=hskey-... NEX_SLUG=osteyia NEX_TOKEN=... \
#     bash -c "$(curl -fsSL https://nex.kiliogene.com/onboard.sh)"
#
# Optional non-interactive vars:
#   NEX_META_TOKEN     read-only token for the nex-meta MCP slug
#   NEX_NO_TAILSCALE   set to skip Tailscale join (already on tailnet)
#
# POSIX bash. Tested on macOS 14+ (Darwin) and Ubuntu 22.04+ (Linux).
# Refuses Windows non-WSL.

set -euo pipefail

# ─────────────────────────────────────────────────────────────────────────────
# Output helpers
# ─────────────────────────────────────────────────────────────────────────────
if [ -t 1 ]; then
  C_OK="\033[1;32m"; C_WARN="\033[1;33m"; C_ERR="\033[1;31m"; C_DIM="\033[2m"; C_OFF="\033[0m"
else
  C_OK=""; C_WARN=""; C_ERR=""; C_DIM=""; C_OFF=""
fi
ok()   { printf "${C_OK}✓${C_OFF} %s\n" "$*"; }
info() { printf "${C_DIM}·${C_OFF} %s\n" "$*"; }
warn() { printf "${C_WARN}!${C_OFF} %s\n" "$*" >&2; }
err()  { printf "${C_ERR}✗${C_OFF} %s\n" "$*" >&2; }

# ─────────────────────────────────────────────────────────────────────────────
# 1. OS detection
# ─────────────────────────────────────────────────────────────────────────────
UNAME_S="$(uname -s)"
case "$UNAME_S" in
  Darwin) OS="darwin" ;;
  Linux)
    if grep -qi microsoft /proc/version 2>/dev/null; then
      err "WSL detected — supported but please use the Linux variant (run from a Linux shell). Aborting."
      exit 1
    fi
    OS="linux" ;;
  *)
    err "Unsupported OS: $UNAME_S (Darwin/Linux only). Windows non-WSL is not supported."
    exit 1 ;;
esac
ok "OS: $OS"

# ─────────────────────────────────────────────────────────────────────────────
# 2. Tailscale install
# ─────────────────────────────────────────────────────────────────────────────
install_tailscale() {
  if command -v tailscale >/dev/null 2>&1; then
    ok "Tailscale already installed ($(tailscale version | head -1))"
    return 0
  fi
  info "Installing Tailscale ..."
  case "$OS" in
    darwin)
      if ! command -v brew >/dev/null 2>&1; then
        err "Homebrew not found. Install it first: https://brew.sh"
        return 1
      fi
      brew install tailscale
      brew services start tailscale 2>/dev/null || true
      ;;
    linux)
      curl -fsSL https://tailscale.com/install.sh | sh
      ;;
  esac
  ok "Tailscale installed"
}

# ─────────────────────────────────────────────────────────────────────────────
# 3. Bun install
# ─────────────────────────────────────────────────────────────────────────────
install_bun() {
  if command -v bun >/dev/null 2>&1; then
    ok "bun already installed ($(bun --version))"
    return 0
  fi
  if [ -x "$HOME/.bun/bin/bun" ]; then
    ok "bun present at \$HOME/.bun/bin/bun — make sure it is on your PATH"
    return 0
  fi
  info "Installing bun ..."
  curl -fsSL https://bun.sh/install | bash
  if [ -x "$HOME/.bun/bin/bun" ]; then
    ok "bun installed at \$HOME/.bun/bin/bun"
    info "Add this to your shell rc: export PATH=\"\$HOME/.bun/bin:\$PATH\""
  else
    err "bun install ran but \$HOME/.bun/bin/bun is missing"
    return 1
  fi
}

# ─────────────────────────────────────────────────────────────────────────────
# 4. Claude Code CLI presence check
# ─────────────────────────────────────────────────────────────────────────────
check_claude_code() {
  if command -v claude >/dev/null 2>&1; then
    ok "Claude Code CLI present ($(claude --version 2>/dev/null | head -1 || echo "version unknown"))"
    return 0
  fi
  err "Claude Code CLI is not installed."
  cat <<EOF >&2

Install Claude Code from https://claude.ai/code  (no, we don't auto-install it
from this script — install your own, sign in, then come back and re-run me.)

Once installed, re-run:
  curl -fsSL https://nex.kiliogene.com/onboard.sh | bash
EOF
  exit 1
}

# ─────────────────────────────────────────────────────────────────────────────
# 5. Collect inputs (interactive or via env vars)
# ─────────────────────────────────────────────────────────────────────────────
collect_inputs() {
  local interactive="no"
  if [ -t 0 ] && [ -t 1 ]; then interactive="yes"; fi

  # Headscale pre-auth key
  if [ -z "${NEX_AUTHKEY:-}" ]; then
    if [ "$interactive" = "yes" ]; then
      printf "Headscale pre-auth key (from Kiliogene admin): " >&2
      read -r NEX_AUTHKEY
    elif [ -n "${NEX_NO_TAILSCALE:-}" ]; then
      info "NEX_NO_TAILSCALE set — skipping authkey"
    else
      err "NEX_AUTHKEY required (or set NEX_NO_TAILSCALE=1 if already on tailnet)"
      exit 1
    fi
  fi

  # NEX slug
  if [ -z "${NEX_SLUG:-}" ]; then
    if [ "$interactive" = "yes" ]; then
      printf "NEX project slug (e.g. osteyia, kerkelt, bip, sam): " >&2
      read -r NEX_SLUG
    else
      err "NEX_SLUG required"
      exit 1
    fi
  fi

  # NEX MCP token
  if [ -z "${NEX_TOKEN:-}" ]; then
    if [ "$interactive" = "yes" ]; then
      printf "NEX_KNOWLEDGE_TOKEN (per-project bearer, from Kiliogene admin): " >&2
      stty -echo 2>/dev/null || true
      read -r NEX_TOKEN
      stty echo 2>/dev/null || true
      printf "\n" >&2
    else
      err "NEX_TOKEN required"
      exit 1
    fi
  fi

  # Optional nex-meta token
  if [ -z "${NEX_META_TOKEN:-}" ] && [ "$interactive" = "yes" ]; then
    printf "NEX_META_TOKEN (optional, shared read-only token, press Enter to skip): " >&2
    stty -echo 2>/dev/null || true
    read -r NEX_META_TOKEN || true
    stty echo 2>/dev/null || true
    printf "\n" >&2
  fi
}

# ─────────────────────────────────────────────────────────────────────────────
# 6. Tailscale join (Headscale Kiliogene)
# ─────────────────────────────────────────────────────────────────────────────
join_tailnet() {
  if [ -n "${NEX_NO_TAILSCALE:-}" ]; then
    info "NEX_NO_TAILSCALE set — skipping tailscale up"
    return 0
  fi
  if [ -z "${NEX_AUTHKEY:-}" ]; then
    warn "no authkey — skipping tailscale up (assume already on tailnet)"
    return 0
  fi
  info "Joining Kiliogene Headscale tailnet ..."
  case "$OS" in
    darwin)
      # macOS: tailscale up via brew install — same CLI as Linux for our purposes
      sudo tailscale up \
        --login-server https://hs.kiliogene.com \
        --auth-key="$NEX_AUTHKEY" \
        --accept-routes
      ;;
    linux)
      sudo tailscale up \
        --login-server https://hs.kiliogene.com \
        --auth-key="$NEX_AUTHKEY" \
        --accept-routes
      ;;
  esac
  ok "Joined tailnet $(tailscale status --self --json 2>/dev/null | grep -o '"HostName":"[^"]*"' | head -1 || echo "(status unknown)")"
}

# ─────────────────────────────────────────────────────────────────────────────
# 7. Write ~/.config/nex/env
# ─────────────────────────────────────────────────────────────────────────────
write_env_file() {
  local dir="$HOME/.config/nex"
  local file="$dir/env"
  mkdir -p "$dir"
  chmod 0700 "$dir"

  if [ -f "$file" ]; then
    if [ -t 0 ]; then
      printf "${C_WARN}!${C_OFF} %s already exists. Overwrite? [y/N] " "$file" >&2
      read -r REPLY
      case "$REPLY" in
        y|Y|yes|YES) info "overwriting" ;;
        *) info "kept existing $file"; return 0 ;;
      esac
    else
      info "non-interactive — overwriting existing $file"
    fi
  fi

  # Atomic write
  local tmp
  tmp="$(mktemp "${file}.XXXXXX")"
  {
    printf '# NEX bootstrap (NEX-ADR-0040). Generated %s.\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
    printf 'export NEX_KNOWLEDGE_TOKEN=%q\n' "$NEX_TOKEN"
    if [ -n "${NEX_META_TOKEN:-}" ]; then
      printf 'export NEX_META_TOKEN=%q\n' "$NEX_META_TOKEN"
    fi
    printf 'export NEX_PROJECT_SLUG=%q\n' "$NEX_SLUG"
    printf 'export CLAUDE_CODE_ENABLE_TELEMETRY=1\n'
    printf 'export OTEL_RESOURCE_ATTRIBUTES=%q\n' "service.namespace=$NEX_SLUG"
  } > "$tmp"
  chmod 0600 "$tmp"
  mv "$tmp" "$file"
  ok "wrote $file (mode 0600)"
}

# ─────────────────────────────────────────────────────────────────────────────
# 8. Inject `source ~/.config/nex/env` into rc shells (idempotent)
# ─────────────────────────────────────────────────────────────────────────────
inject_rc_source() {
  local source_line='[ -f "$HOME/.config/nex/env" ] && source "$HOME/.config/nex/env"'
  local marker='# NEX bootstrap — source env'
  local injected=0

  # Candidates: ~/.zshrc always; ~/.zprofile only if it already exists.
  local rc_files=("$HOME/.zshrc")
  [ -f "$HOME/.zprofile" ] && rc_files+=("$HOME/.zprofile")

  for rc in "${rc_files[@]}"; do
    # Create ~/.zshrc if it doesn't exist yet.
    touch "$rc"
    # Idempotent: skip if the source line (or marker) is already present.
    if grep -qF '.config/nex/env' "$rc" 2>/dev/null; then
      info "$(basename "$rc"): source ~/.config/nex/env already present — skipped"
      continue
    fi
    printf '\n%s\n%s\n' "$marker" "$source_line" >> "$rc"
    ok "$(basename "$rc"): added 'source ~/.config/nex/env'"
    injected=$((injected + 1))
  done

  if [ "$injected" -eq 0 ]; then
    info "rc shells already up-to-date — nothing to inject"
  fi
}

# ─────────────────────────────────────────────────────────────────────────────
# 10. Verify tailnet access to nex-knowledge MCP
# ─────────────────────────────────────────────────────────────────────────────
verify_tailnet_access() {
  if [ -n "${NEX_NO_TAILSCALE:-}" ]; then
    info "NEX_NO_TAILSCALE set — skipping verification"
    return 0
  fi
  info "Verifying tailnet access to nex-knowledge MCP ..."
  local i=0
  while [ "$i" -lt 3 ]; do
    if curl -fsS --max-time 5 "http://nex.hs.kiliogene.com/healthz" >/dev/null 2>&1; then
      ok "nex-knowledge reachable on tailnet"
      return 0
    fi
    i=$((i + 1))
    info "  attempt $i/3 failed, retrying in 5s ..."
    sleep 5
  done
  warn "nex-knowledge not reachable yet. Check 'tailscale status' and that the Kiliogene admin authorised your node."
  return 1
}

# ─────────────────────────────────────────────────────────────────────────────
# 11. Next steps banner
# ─────────────────────────────────────────────────────────────────────────────
next_steps() {
  cat <<EOF

──────────────────────────────────────────────────────────────────────────
  ${C_OK}NEX bootstrap done.${C_OFF}

  Next:

    1) Open a new terminal (or: ${C_DIM}source ~/.config/nex/env${C_OFF}).
       ~/.config/nex/env is now sourced automatically by your rc shell.

    2) Start Claude Code:
       ${C_DIM}claude${C_OFF}

    3) In the first session, type:
       ${C_DIM}/nex-onboard-me${C_OFF}
       This finalises calibration (memory, hooks, project CLAUDE.md).

  Reference: NEX-ADR-0040 (this bootstrap), NEX-ADR-0006 (MCP).
──────────────────────────────────────────────────────────────────────────
EOF
}

# ─────────────────────────────────────────────────────────────────────────────
# Main
# ─────────────────────────────────────────────────────────────────────────────
main() {
  printf "${C_OK}NEX bootstrap${C_OFF} — Kiliogene\n"
  install_tailscale
  install_bun
  check_claude_code
  collect_inputs
  join_tailnet || warn "tailscale up failed — re-run if needed"
  write_env_file
  inject_rc_source
  verify_tailnet_access || warn "MCP verification deferred — relaunch the verify section after fixing tailnet"
  next_steps
}

main "$@"
