#!/usr/bin/env bash
# ferrosa-memory fast setup — installs prebuilt binaries via the LATEST file,
# downloads ONBOARDING.md, optionally clones source repos, optionally pulls
# the Nomic embedding model, and hands off to a selected LLM harness.
#
# Reads https://ferrosadb.com/LATEST (a plain-text version tag like "v0.18.0")
# for the ferrosa release artifact and https://ferrosadb.com/LATEST-MEMORY
# (e.g. "v0.24.0") for the ferrosa-memory artifact — the two projects version
# independently. No source compile.
#
# Usage:
#   curl -fsSL https://ferrosadb.com/setup-memory.sh | bash
#   curl -fsSL https://ferrosadb.com/setup-memory.sh | bash -s -- --version v0.16.0 --no-clone
#
# Env overrides (mostly for testing):
#   FERROSA_LATEST_URL    — ferrosa version pointer (default https://ferrosadb.com/LATEST)
#   MEMORY_LATEST_URL     — ferrosa-memory version pointer (default https://ferrosadb.com/LATEST-MEMORY)
#   FERROSA_RELEASE_HOST  — ferrosa releases root
#   MEMORY_RELEASE_HOST   — ferrosa-memory releases root
#   ONBOARDING_URL        — ONBOARDING.md source (default github raw on main)
#   FERROSA_SUITE_DIR     — where to put cloned repos (default $HOME/src/ferrosa-suite)
#   FERROSA_INSTALL_ROOT  — binary install prefix (default $HOME/.ferrosa)
#   NOMIC_MODEL           — embedding model name (default nomic-embed-text-v2-moe)
set -euo pipefail

FERROSA_REPO="ferrosadb/ferrosa"
MEMORY_REPO="ferrosadb/ferrosa-memory"
LATEST_URL="${FERROSA_LATEST_URL:-https://ferrosadb.com/LATEST}"
MEMORY_LATEST_URL="${MEMORY_LATEST_URL:-https://ferrosadb.com/LATEST-MEMORY}"
FERROSA_RELEASE_HOST="${FERROSA_RELEASE_HOST:-https://github.com/${FERROSA_REPO}/releases}"
MEMORY_RELEASE_HOST="${MEMORY_RELEASE_HOST:-https://github.com/${MEMORY_REPO}/releases}"
ONBOARDING_URL="${ONBOARDING_URL:-https://raw.githubusercontent.com/${MEMORY_REPO}/main/ONBOARDING.md}"
FERROSA_SUITE_DIR="${FERROSA_SUITE_DIR:-$HOME/src/ferrosa-suite}"
INSTALL_ROOT="${FERROSA_INSTALL_ROOT:-${HOME}/.ferrosa}"
BIN_DIR="${INSTALL_ROOT}/bin"
CONFIG_DIR="${INSTALL_ROOT}/config"
DATA_DIR="${INSTALL_ROOT}/data"
LOG_DIR="${INSTALL_ROOT}/logs"
NOMIC_MODEL="${NOMIC_MODEL:-nomic-embed-text-v2-moe}"

VERSION=""
MEMORY_VERSION=""
WANT_CLONE=""    # ask|yes|no
WANT_NOMIC=""    # ask|yes|no
WANT_HERMES=""   # ask|yes|no
WANT_HOOKS=""    # ask|yes|no — install harness hooks (default yes)
HARNESS="${FERROSA_HARNESS:-auto}"   # auto|all|codex|claude|hermes|generic
MCP_URL="${FERROSA_MEMORY_MCP_URL:-http://127.0.0.1:18765/mcp}"

while [ $# -gt 0 ]; do
  case "$1" in
    --version)        VERSION="$2"; shift 2 ;;
    --memory-version) MEMORY_VERSION="$2"; shift 2 ;;
    --clone)      WANT_CLONE="yes"; shift ;;
    --no-clone)   WANT_CLONE="no"; shift ;;
    --nomic)      WANT_NOMIC="yes"; shift ;;
    --no-nomic)   WANT_NOMIC="no"; shift ;;
    --hermes)     WANT_HERMES="yes"; shift ;;
    --no-hermes)  WANT_HERMES="no"; shift ;;
    --hooks)      WANT_HOOKS="yes"; shift ;;
    --no-hooks)   WANT_HOOKS="no"; shift ;;
    --harness)    HARNESS="$2"; shift 2 ;;
    --mcp-url)    MCP_URL="$2"; shift 2 ;;
    -h|--help)
      cat <<EOF
ferrosa-memory fast setup
  --version <tag>          ferrosa tag to install (default: read $LATEST_URL)
  --memory-version <tag>   ferrosa-memory tag to install (default: read $MEMORY_LATEST_URL)
  --clone / --no-clone     clone or update source repos under \$FERROSA_SUITE_DIR
  --nomic / --no-nomic     pull the Nomic embedding model via ollama
  --hooks / --no-hooks     install LLM-harness hooks (session-start/recall/turn)
  --harness <name>         which harness hooks to install: auto|all|codex|claude|hermes|generic
  --mcp-url <url>          MCP endpoint baked into the hooks (default $MCP_URL)
  --hermes / --no-hermes   exec hermes "onboard me ..." when done
EOF
      exit 0 ;;
    *) echo "unknown flag: $1" >&2; exit 2 ;;
  esac
done

say() { printf ':: %s\n' "$*" >&2; }
die() { printf 'error: %s\n' "$*" >&2; exit 1; }

detect_target() {
  local os arch
  os=$(uname -s); arch=$(uname -m)
  case "$os/$arch" in
    Darwin/arm64)              echo "aarch64-apple-darwin" ;;
    Darwin/x86_64)
      die "Intel macOS is not supported in v0.x. Build from source: https://github.com/${MEMORY_REPO}#building" ;;
    Linux/x86_64)              echo "x86_64-unknown-linux-musl" ;;
    Linux/aarch64|Linux/arm64) echo "aarch64-unknown-linux-musl" ;;
    *) die "unsupported platform: $os/$arch" ;;
  esac
}
TARGET=$(detect_target)

if [ -z "$VERSION" ]; then
  say "resolving latest ferrosa version from $LATEST_URL"
  VERSION=$(curl -fsSL "$LATEST_URL" | tr -d '[:space:]')
fi
[ -n "$VERSION" ] || die "no version resolved from $LATEST_URL"
case "$VERSION" in
  v*) : ;;
  *)  VERSION="v${VERSION}" ;;
esac

if [ -z "$MEMORY_VERSION" ]; then
  say "resolving latest ferrosa-memory version from $MEMORY_LATEST_URL"
  MEMORY_VERSION=$(curl -fsSL "$MEMORY_LATEST_URL" | tr -d '[:space:]')
fi
[ -n "$MEMORY_VERSION" ] || die "no version resolved from $MEMORY_LATEST_URL"
case "$MEMORY_VERSION" in
  v*) : ;;
  *)  MEMORY_VERSION="v${MEMORY_VERSION}" ;;
esac

prompt_yes() {
  local q="$1" a
  if [ ! -t 0 ] && [ ! -r /dev/tty ]; then
    return 1
  fi
  read -r -p "$q [y/N] " a < /dev/tty
  case "${a:-N}" in y|Y|yes|Yes|YES) return 0 ;; *) return 1 ;; esac
}

# ── Stage 1: install binaries ───────────────────────────────────────────────
install_tarball() {
  local label="$1" host="$2" tarball="$3" version="$4"
  local url="${host}/download/${version}/${tarball}"
  local sums_url="${host}/download/${version}/SHA256SUMS"
  local tmp; tmp=$(mktemp -d)
  say "downloading ${label} ${tarball}"
  curl -fsSL --output "$tmp/$tarball" "$url" >&2
  curl -fsSL --output "$tmp/SHA256SUMS" "$sums_url" >&2
  ( cd "$tmp" && grep "$tarball" SHA256SUMS | shasum -a 256 -c - >&2 ) \
    || die "${label}: checksum verification FAILED"
  tar -xzf "$tmp/$tarball" -C "$tmp" >&2
  printf '%s\n' "$tmp"
}

mkdir -p "$BIN_DIR" "$CONFIG_DIR" "$DATA_DIR" "$LOG_DIR"

FERROSA_TARBALL="ferrosa-${VERSION}-${TARGET}.tar.gz"
MEMORY_TARBALL="ferrosa-memory-${MEMORY_VERSION}-${TARGET}.tar.gz"

# ferrosa binary
F_TMP=$(install_tarball "ferrosa" "$FERROSA_RELEASE_HOST" "$FERROSA_TARBALL" "$VERSION")
cp "$F_TMP/ferrosa"     "$BIN_DIR/"
cp "$F_TMP/ferrosa-ctl" "$BIN_DIR/"
chmod +x "$BIN_DIR/ferrosa" "$BIN_DIR/ferrosa-ctl"
if [ ! -f "$CONFIG_DIR/ferrosa.toml" ]; then
  cp "$F_TMP/config/ferrosa.example.toml" "$CONFIG_DIR/ferrosa.toml"
fi
rm -rf "$F_TMP"

# ferrosa-memory binary
M_TMP=$(install_tarball "ferrosa-memory" "$MEMORY_RELEASE_HOST" "$MEMORY_TARBALL" "$MEMORY_VERSION")
cp "$M_TMP/ferrosa-memory-mcp" "$BIN_DIR/"
chmod +x "$BIN_DIR/ferrosa-memory-mcp"
if [ ! -f "$CONFIG_DIR/ferrosa-memory.toml" ]; then
  cp "$M_TMP/config/ferrosa-memory.example.toml" "$CONFIG_DIR/ferrosa-memory.toml"
fi
rm -rf "$M_TMP"

# ── Stage 2: optional source clone ──────────────────────────────────────────
clone_or_update() {
  local url="$1" dir="$2"
  if [ -d "$dir/.git" ]; then
    say "updating $dir"
    git -C "$dir" fetch --all --prune
  else
    say "cloning $url -> $dir"
    git clone "$url" "$dir"
  fi
}

do_clone() {
  mkdir -p "$FERROSA_SUITE_DIR"
  clone_or_update "https://github.com/${FERROSA_REPO}.git" "$FERROSA_SUITE_DIR/ferrosa"
  clone_or_update "https://github.com/${MEMORY_REPO}.git" "$FERROSA_SUITE_DIR/ferrosa-memory"
}

case "$WANT_CLONE" in
  yes) do_clone ;;
  no)  : ;;
  "")  prompt_yes "Clone or update source repos at $FERROSA_SUITE_DIR?" && do_clone ;;
esac

# ── Stage 3: ONBOARDING.md ──────────────────────────────────────────────────
ONBOARDING_DIR="$FERROSA_SUITE_DIR/ferrosa-memory"
ONBOARDING_PATH="$ONBOARDING_DIR/ONBOARDING.md"
mkdir -p "$ONBOARDING_DIR"
if [ ! -f "$ONBOARDING_PATH" ]; then
  say "downloading ONBOARDING.md from $ONBOARDING_URL"
  curl -fsSL "$ONBOARDING_URL" -o "$ONBOARDING_PATH" \
    || say "failed to fetch ONBOARDING.md (continuing; you can re-download later)"
fi

# ── Stage 3b: install LLM-harness hooks ─────────────────────────────────────
# The hooks (session-start / recall / turn-finalization) are what make the
# memory server actually engage the LLM — without them onboarding is incomplete.
# They are installed by ferrosa-memory's self-contained hook installer
# (scripts/install-agent-hooks.py + scripts/hooks/ferrosa-memory-turn-hook.py,
# both stdlib-only Python). With a source checkout we run it in-tree; with
# --no-clone we fetch those two files PINNED to $MEMORY_VERSION into a stable
# location
# (the generated wrappers bake in the hook path, so it must persist) and run
# them against the installed MCP endpoint.
HOOK_SRC_DIR="${INSTALL_ROOT}/share/ferrosa-memory"
RAW_BASE="https://raw.githubusercontent.com/${MEMORY_REPO}/${MEMORY_VERSION}"

fetch_hook_installer() {
  # Pinned to the release tag so hooks match the installed binary; fails loud
  # (no silent fallback to main) if the tag lacks the files.
  mkdir -p "$HOOK_SRC_DIR/scripts/hooks"
  curl -fsSL "$RAW_BASE/scripts/install-agent-hooks.py" \
       -o "$HOOK_SRC_DIR/scripts/install-agent-hooks.py" || return 1
  curl -fsSL "$RAW_BASE/scripts/hooks/ferrosa-memory-turn-hook.py" \
       -o "$HOOK_SRC_DIR/scripts/hooks/ferrosa-memory-turn-hook.py" || return 1
}

install_hooks() {
  if ! command -v python3 >/dev/null 2>&1; then
    say "python3 not found — cannot install harness hooks automatically."
    say "Install python3, then run:"
    say "  curl -fsSL $RAW_BASE/scripts/install-agent-hooks.py | \\"
    say "    python3 - --harness $HARNESS --mcp-url $MCP_URL"
    return 1
  fi
  local root
  if [ -f "$FERROSA_SUITE_DIR/ferrosa-memory/scripts/install-agent-hooks.py" ]; then
    root="$FERROSA_SUITE_DIR/ferrosa-memory"
    say "installing harness hooks from source checkout (harness=$HARNESS)"
  else
    say "fetching harness hook installer @ $MEMORY_VERSION (no source checkout)"
    if ! fetch_hook_installer; then
      say "could not fetch the hook installer at $MEMORY_VERSION."
      say "Fix: clone the repo and run ./setup.sh, or fetch manually from"
      say "  $RAW_BASE/scripts/install-agent-hooks.py"
      return 1
    fi
    root="$HOOK_SRC_DIR"
  fi
  # No --skip-auth-check: it post-dates older release installers, and the
  # installer already warns-and-continues when the server is unreachable (and
  # correctly refuses if the server is up but credentials are inconsistent).
  if ( cd "$root" && python3 scripts/install-agent-hooks.py \
         --harness "$HARNESS" --mcp-url "$MCP_URL" ); then
    say "harness hooks installed (harness=$HARNESS, mcp=$MCP_URL)"
  else
    say "hook installer reported an error (see output above)"
    return 1
  fi
}

if [ "$WANT_HOOKS" = "no" ]; then
  say "skipping harness hook installation (--no-hooks)"
else
  install_hooks || say "WARNING: harness hooks were NOT installed (see above). The memory \
server will run, but your LLM will not auto-recall/ingest until hooks are installed."
fi

# ── Stage 4: optional Nomic embedding model ─────────────────────────────────
pull_nomic() {
  if command -v ollama >/dev/null 2>&1; then
    say "pulling $NOMIC_MODEL via ollama"
    ollama pull "$NOMIC_MODEL"
  else
    say "ollama not found — skipping. Install ollama and run: ollama pull $NOMIC_MODEL"
  fi
}

case "$WANT_NOMIC" in
  yes) pull_nomic ;;
  no)  : ;;
  "")  if command -v ollama >/dev/null 2>&1; then
         prompt_yes "Pull Nomic embedding model ($NOMIC_MODEL) for semantic search?" \
           && pull_nomic
       else
         say "ollama not found — skipping embedding model. Semantic/vector search will be degraded."
       fi ;;
esac

# ── Stage 5: hand off to LLM harness ────────────────────────────────────────
cat <<EOF >&2

ferrosa-memory $MEMORY_VERSION installed (ferrosa $VERSION).

  binaries: $BIN_DIR
  config:   $CONFIG_DIR/ferrosa-memory.toml
  hooks:    ~/.config/ferrosa-memory/hooks (harness=$HARNESS; unless --no-hooks)
  onboard:  $ONBOARDING_PATH

EOF

case "$WANT_HERMES" in
  yes) command -v hermes >/dev/null 2>&1 && exec hermes "onboard me using $ONBOARDING_PATH" ;;
  no)  : ;;
  "")  if command -v hermes >/dev/null 2>&1 \
         && prompt_yes "Launch Hermes with the onboard-me prompt now?"; then
         exec hermes "onboard me using $ONBOARDING_PATH"
       fi ;;
esac

cat <<EOF >&2
Next: run your preferred LLM harness with the onboard-me prompt.

Hermes:
  hermes "onboard me using $ONBOARDING_PATH"

Claude Code / Codex / another harness — paste at the prompt:
  onboard me using $ONBOARDING_PATH

The onboarding prompt walks through native vs Compose runtime, skills, hooks,
credentials, and ports.
EOF
