#!/usr/bin/env bash
# >we1re_ — the one command that brings up a P2PTB swarm from scratch.
#
# What it does:
#   1. Plays a >we1re_ boot animation (because drama).
#   2. Prompts for your OpenAI key and validates it against api.openai.com.
#      (One key drives both gpt-4o-mini brain and Qdrant embeddings.)
#   3. Installs OS packages, gh, Go, Qdrant.
#   4. Downloads a prebuilt swarm bundle (bridge + Agent-Harness + Agent-CLI).
#   5. Builds the bridge daemon + bridge-cli, sets up the harness venv.
#   6. Generates an admin token; starts Qdrant + bridge in the background.
#   7. Registers your bridge-cli identity.
#   8. Scaffolds and starts demo-agent — an OpenAI-powered peer.
#
# Prereqs:
#   - Ubuntu 24.04 (clean container or VM)
#   - Network access (to fetch the swarm bundle, Go, and Qdrant)
#   - An OpenAI API key
#
# Env knobs:
#   NO_ANIM=1     skip the boot animation
#   NO_PAUSE=1    skip the press-enter prompts between stages
#   SKIP_AGENT=1  stop after the bridge is up (no demo-agent, no key prompt)
#   NO_CLI=1      don't launch bridge-cli at the end
#
# Idempotent — safe to re-run. Skips work that's already done.

set -euo pipefail
export DEBIAN_FRONTEND=noninteractive
export PATH="$PATH:/usr/local/go/bin:$HOME/go/bin:$HOME/.local/bin"

GO_VERSION="${GO_VERSION:-1.26.0}"
WORKDIR="${WORKDIR:-$HOME/agents}"
P2PTB_HOME="${P2PTB_HOME:-$HOME/.p2ptb}"
AGENT_ID="${AGENT_ID:-demo-agent}"
BUNDLE_URL="${BUNDLE_URL:-https://fidusunos-mac-mini.tail41aa9a.ts.net/swarm-bundle.tar.gz}"
SPAWN_URL="${SPAWN_URL:-https://fidusunos-mac-mini.tail41aa9a.ts.net/spawn.sh}"

HARNESS_DIR="$WORKDIR/Agent-Harness"
BRIDGE_BIN="$WORKDIR/bridge/bridge/bridge"
BRIDGE_LOG_DIR="$P2PTB_HOME/bridge"
BRIDGE_LOG="$BRIDGE_LOG_DIR/run.log"
BRIDGE_PID_FILE="$BRIDGE_LOG_DIR/bridge.pid"
HEALTH_URL="https://localhost:8766/healthz"

QDRANT_DIR="$P2PTB_HOME/qdrant"
QDRANT_PID_FILE="$QDRANT_DIR/qdrant.pid"
QDRANT_LOG="$QDRANT_DIR/run.log"
QDRANT_URL="http://localhost:6333"

AGENT_LOG_DIR="$P2PTB_HOME/$AGENT_ID"
AGENT_LOG="$AGENT_LOG_DIR/agent.log"
AGENT_PID_FILE="$AGENT_LOG_DIR/agent.pid"

log() { printf '\n############ %s ############\n' "$*"; }
sub() { printf '\n=== %s ===\n' "$*"; }
pause() {
    if [[ "${NO_PAUSE:-0}" != "1" ]]; then
        printf '\n[press enter to continue — or set NO_PAUSE=1 to skip pauses] '
        read -r _
    fi
}

# --- >we1re_ boot animation ------------------------------------------------
animate_logo() {
    if [[ ! -t 1 ]] || [[ "${NO_ANIM:-0}" == "1" ]]; then
        return 0
    fi

    local cols rows
    cols=$(tput cols 2>/dev/null || echo 80)
    rows=$(tput lines 2>/dev/null || echo 24)
    if (( cols < 70 || rows < 14 )); then
        return 0
    fi

    local G_GT=$'██╗    \n╚██╗   \n ╚██╗  \n ██╔╝  \n██╔╝   \n╚═╝    '
    local G_W=$'██╗    ██╗  \n██║    ██║  \n██║ █╗ ██║  \n██║███╗██║  \n╚███╔███╔╝  \n ╚══╝╚══╝   '
    local G_E=$'███████╗  \n██╔════╝  \n█████╗    \n██╔══╝    \n███████╗  \n╚══════╝  '
    local G_1=$' ██╗  \n███║  \n╚██║  \n ██║  \n ██║  \n ╚═╝  '
    local G_R=$'          \n██████╗   \n██╔══██╗  \n██████╔╝  \n██╔══██╗  \n╚═╝  ╚═╝  '
    local G_U=$'          \n          \n          \n          \n          \n████████╗ '
    local BLANK6=$'      \n      \n      \n      \n      \n      '

    local W_GT=7 W_W=12 W_E=10 W_1=6 W_R=10 W_U=10
    local total=$(( W_GT + W_W + W_E + W_1 + W_R + W_E + W_U ))
    local start_col=$(( (cols - total) / 2 + 1 ))
    local start_row=$(( (rows - 6) / 2 ))

    local col_gt=$start_col
    local col_w=$(( col_gt + W_GT ))
    local col_e1=$(( col_w + W_W ))
    local col_1=$(( col_e1 + W_E ))
    local col_r=$(( col_1 + W_1 ))
    local col_e2=$(( col_r + W_R ))
    local col_u=$(( col_e2 + W_E ))

    local HIDE=$'\033[?25l' SHOW=$'\033[?25h'
    local CLR=$'\033[2J\033[H' RST=$'\033[0m'
    local BOLD=$'\033[1m'
    local GRN=$'\033[32m' BRT=$'\033[92m' CYN=$'\033[96m' YEL=$'\033[93m' MAG=$'\033[95m'

    pos() { printf '\033[%d;%dH' "$1" "$2"; }

    render() {
        local color=$1 col=$2 glyph=$3 i=0 line
        while IFS= read -r line; do
            pos $(( start_row + i )) "$col"
            printf '%s%s%s' "$color" "$line" "$RST"
            ((i++)) || true
        done <<< "$glyph"
    }

    thin_cursor() {
        local color=$1 col=$2 ch=$3 i
        for i in 0 1 2 3 4 5; do
            pos $(( start_row + i )) "$col"
            printf '%s%s%s' "$color" "$ch" "$RST"
        done
    }

    printf '%s%s' "$HIDE" "$CLR"
    trap 'printf "%s%s\n" "$SHOW" "$RST"' INT TERM

    local cursor_col=$(( col_1 + 1 ))
    local i
    for i in 1 2 3 4; do
        thin_cursor "$BRT" "$cursor_col" "▌"; sleep 0.26
        thin_cursor "" "$cursor_col" " ";    sleep 0.26
    done

    render "$BOLD$YEL" "$col_1" "$G_1"; sleep 0.07
    render "$BOLD$MAG" "$col_1" "$G_1"; sleep 0.07
    render "$BOLD$CYN" "$col_1" "$G_1"; sleep 0.07
    render "$BOLD$BRT" "$col_1" "$G_1"; sleep 0.35

    render "$GRN" "$col_gt" "$G_GT"; sleep 0.18
    render "$GRN" "$col_w"  "$G_W";  sleep 0.18
    render "$GRN" "$col_e1" "$G_E";  sleep 0.28

    for i in 1 2 3; do
        render "" "$col_1" "$BLANK6"; sleep 0.13
        render "$BOLD$YEL" "$col_1" "$G_1"; sleep 0.13
    done
    render "$BOLD$BRT" "$col_1" "$G_1"; sleep 0.2

    render "$GRN" "$col_r"  "$G_R"; sleep 0.18
    render "$GRN" "$col_e2" "$G_E"; sleep 0.30

    for i in 1 2 3; do
        render "$BRT" "$col_u" "$G_U"; sleep 0.32
        render "" "$col_u" "$BLANK6";  sleep 0.32
    done
    render "$BRT" "$col_u" "$G_U"; sleep 0.6

    trap - INT TERM
    printf '%s%s' "$SHOW" "$CLR"
}

# --- OpenAI key validation -------------------------------------------------
_validate_openai_key() {
    local code
    code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 \
        -H "Authorization: Bearer $1" \
        https://api.openai.com/v1/models 2>/dev/null || true)
    case "$code" in
        200)        echo ok ;;
        401|403)    echo bad ;;
        *)          echo "neterr:${code:-000}" ;;
    esac
}

# ============================================================================
# Stage 0 — animation
# ============================================================================
animate_logo

# ============================================================================
# Stage 1 — OpenAI key prompt + validation
# ============================================================================
if [[ "${SKIP_AGENT:-0}" != "1" ]]; then
    if [[ -n "${OPENAI_API_KEY:-}" ]]; then
        printf '\nOPENAI_API_KEY is set in env — validating with OpenAI... '
        case "$(_validate_openai_key "$OPENAI_API_KEY")" in
            ok)         echo "ok" ;;
            bad)        echo "rejected (401/403). Unsetting; will prompt for a fresh one."
                        unset OPENAI_API_KEY ;;
            neterr:*)   echo "couldn't reach OpenAI — accepting on trust" ;;
        esac
    fi

    attempts=0
    while [[ -z "${OPENAI_API_KEY:-}" ]] && (( attempts < 3 )); do
        attempts=$(( attempts + 1 ))
        printf '\nOPENAI_API_KEY is not set. It drives the demo-agent brain + Qdrant embeddings.\n'
        printf 'Paste it now (input hidden), or press enter to continue without the daemon: '
        read -rs _candidate
        printf '\n'
        if [[ -z "$_candidate" ]]; then
            echo "  no key entered — will run install + bridge, then stop before the agent"
            SKIP_AGENT=1
            break
        fi
        printf '  validating with OpenAI... '
        case "$(_validate_openai_key "$_candidate")" in
            ok)
                echo "ok"
                export OPENAI_API_KEY="$_candidate"
                echo "  key captured (${#_candidate} chars) — will start demo-agent at the end"
                ;;
            bad)
                echo "rejected by OpenAI (401/403) — try again (attempt ${attempts}/3)"
                ;;
            neterr:*)
                echo "couldn't reach OpenAI — accepting on trust"
                export OPENAI_API_KEY="$_candidate"
                echo "  key captured (${#_candidate} chars) — will start demo-agent at the end"
                ;;
        esac
    done
    unset _candidate
    if [[ -z "${OPENAI_API_KEY:-}" ]] && [[ "${SKIP_AGENT:-0}" != "1" ]]; then
        echo "  3 invalid keys — continuing without the daemon"
        SKIP_AGENT=1
    fi
fi

# ============================================================================
# Stage 2 — install: OS packages, gh, Go, repos, bridge, Qdrant, harness, cli
# ============================================================================
log "1/3  install — OS packages, gh, Go, repos, bridge, Qdrant, harness, cli"

sub "Installing OS packages"
sudo -E apt-get update -qq
sudo -E apt-get install -y --no-install-recommends \
    python3 python3-pip python3-venv \
    git build-essential ca-certificates curl wget tar make openssl


NEED_GO=1
if command -v go >/dev/null 2>&1; then
    cur="$(go version | awk '{print $3}')"
    if [[ "$cur" == "go${GO_VERSION}"* ]] || [[ "$cur" > "go${GO_VERSION}" ]]; then
        NEED_GO=0
    fi
fi
if [[ "$NEED_GO" == 1 ]]; then
    sub "Installing Go ${GO_VERSION}"
    case "$(dpkg --print-architecture)" in
        amd64) GOARCH=amd64 ;;
        arm64) GOARCH=arm64 ;;
        *) echo "Unsupported arch: $(dpkg --print-architecture)" >&2; exit 1 ;;
    esac
    cd /tmp
    wget -q "https://go.dev/dl/go${GO_VERSION}.linux-${GOARCH}.tar.gz"
    sudo rm -rf /usr/local/go
    sudo tar -C /usr/local -xzf "go${GO_VERSION}.linux-${GOARCH}.tar.gz"
    rm "go${GO_VERSION}.linux-${GOARCH}.tar.gz"
fi
if ! grep -q '/usr/local/go/bin' "$HOME/.bashrc" 2>/dev/null; then
    echo 'export PATH=$PATH:/usr/local/go/bin:$HOME/go/bin:$HOME/.local/bin' >> "$HOME/.bashrc"
fi

sub "Fetching swarm bundle"
mkdir -p "$WORKDIR"
cd "$WORKDIR"
BUNDLE_TGZ="$WORKDIR/swarm-bundle.tar.gz"
if [[ -f "$BUNDLE_TGZ" ]]; then
    echo "  bundle already downloaded, reusing $BUNDLE_TGZ"
else
    echo "  downloading swarm bundle from:"
    echo "    $BUNDLE_URL"
    if ! curl -fSL --retry 3 --connect-timeout 20 -o "$BUNDLE_TGZ" "$BUNDLE_URL"; then
        echo "  ERROR: bundle download failed." >&2
        echo "  The link is time-limited and may have expired. Ask for a fresh URL." >&2
        rm -f "$BUNDLE_TGZ"
        exit 1
    fi
fi
for name in bridge Agent-Harness Agent-CLI; do
    if [[ -d "$WORKDIR/$name" ]]; then
        echo "  $name already extracted, skipping"
    else
        echo "  extracting $name"
        tar -xzf "$BUNDLE_TGZ" -C "$WORKDIR" "$name"
    fi
done

sub "Building bridge"
cd "$WORKDIR/bridge"
make build

# Patch the SDK so harness daemons register with type=agent. Upstream the
# harness's _register() payload omits type, so the bridge stores
# declared_type=NULL and reports it back as "service" — which makes the
# bridge-cli TUI's peer tree hide the agent. Idempotent.
SDK_FILE="$WORKDIR/bridge/p2ptb/bridge_client.py"
if [[ -f "$SDK_FILE" ]] && ! grep -q '"type": "agent"' "$SDK_FILE"; then
    sub "Patching SDK: declare type=agent on registration"
    python3 - "$SDK_FILE" <<'PY'
import pathlib, sys
p = pathlib.Path(sys.argv[1])
src = p.read_text()
needle = '            "api_key_hash": hashlib.sha256(self.config.auth_mode.encode()).hexdigest(),'
if needle in src and '"type": "agent"' not in src:
    src = src.replace(needle, needle + '\n            "type": "agent",')
    p.write_text(src)
    print("  patched bridge_client.py")
else:
    print("  could not find injection point (upstream changed?) — skipping")
PY
fi

mkdir -p "$P2PTB_HOME"
if [[ ! -f "$P2PTB_HOME/admin_token.txt" ]]; then
    sub "Generating admin token"
    openssl rand -hex 32 > "$P2PTB_HOME/admin_token.txt"
    chmod 600 "$P2PTB_HOME/admin_token.txt"
fi

if [[ ! -x /usr/local/bin/qdrant ]]; then
    sub "Installing Qdrant (vector DB for memory)"
    case "$(dpkg --print-architecture)" in
        arm64) QDRANT_TARBALL=qdrant-aarch64-unknown-linux-musl.tar.gz ;;
        amd64) QDRANT_TARBALL=qdrant-x86_64-unknown-linux-gnu.tar.gz ;;
        *) echo "Unsupported arch for Qdrant: $(dpkg --print-architecture)" >&2; exit 1 ;;
    esac
    # python3 consumes the full stream — avoids SIGPIPE / pipefail blow-ups
    # that `grep -m1 | cut` runs into.
    QDRANT_VERSION="$(curl -fsSL https://api.github.com/repos/qdrant/qdrant/releases/latest \
        | python3 -c 'import json,sys; print(json.load(sys.stdin)["tag_name"])')"
    if [[ -z "$QDRANT_VERSION" ]]; then
        echo "Couldn't resolve latest Qdrant version from GitHub API" >&2
        exit 1
    fi
    echo "  version: $QDRANT_VERSION  tarball: $QDRANT_TARBALL"
    cd /tmp
    rm -rf qdrant-extract
    mkdir -p qdrant-extract
    wget -q "https://github.com/qdrant/qdrant/releases/download/${QDRANT_VERSION}/${QDRANT_TARBALL}"
    tar xzf "$QDRANT_TARBALL" -C qdrant-extract
    QDRANT_BIN="$(find qdrant-extract -name qdrant -type f -executable | head -n1)"
    if [[ -z "$QDRANT_BIN" ]]; then
        echo "Failed to find qdrant binary inside tarball" >&2
        exit 1
    fi
    sudo install -m 0755 "$QDRANT_BIN" /usr/local/bin/qdrant
    rm -rf qdrant-extract "$QDRANT_TARBALL"
fi
mkdir -p "$QDRANT_DIR/storage" "$QDRANT_DIR/snapshots"

sub "Setting up harness (with [all] extras: openai, google, bridge/qdrant)"
cd "$HARNESS_DIR"
if [[ -d "$WORKDIR/bridge/.git" ]]; then
    SDK_SOURCE_REPO="$WORKDIR/bridge" bash scripts/sync-sdk.sh
else
    echo "  bundle has no git metadata, using the harness vendored SDK (p2ptb/)"
fi
if [[ ! -d .venv ]]; then
    python3 -m venv .venv
fi
# shellcheck disable=SC1091
source .venv/bin/activate
pip install --quiet --upgrade pip
pip install --quiet -e ".[all]"
# pynacl is required by p2ptb/crypto.py but missing from the harness's
# pyproject extras (upstream bug — CA/Fidus boxes have it from elsewhere).
pip install --quiet pynacl
# pyoxigraph — substrate for the mnemos graph-memory broker. Non-fatal:
# if the wheel is unavailable the core swarm still comes up on Qdrant.
pip install --quiet pyoxigraph==0.5.8 || echo "  WARNING: pyoxigraph install failed — graph memory will be unavailable" >&2
deactivate

sub "Building bridge-cli"
cd "$WORKDIR/Agent-CLI"
go build -o bridge-cli ./cmd/bridge-cli
mkdir -p "$HOME/.local/bin"
install -m 0755 bridge-cli "$HOME/.local/bin/bridge-cli"
# Also install to /usr/local/bin (on every shell's PATH by default) so
# `bridge-cli` works immediately in any shell, no PATH/login dance.
sudo install -m 0755 bridge-cli /usr/local/bin/bridge-cli 2>/dev/null || true

sub "Installing spawn helper (type 'spawn <name>' to add more agents)"
if curl -fSL --retry 3 --connect-timeout 20 -o "$HOME/.local/bin/spawn" "$SPAWN_URL"; then
    chmod +x "$HOME/.local/bin/spawn"
    sudo install -m 0755 "$HOME/.local/bin/spawn" /usr/local/bin/spawn 2>/dev/null || true
    echo "  spawn installed to ~/.local/bin/spawn (and /usr/local/bin)"
else
    echo "  WARNING: could not fetch spawn helper from $SPAWN_URL (skipping)" >&2
fi

pause

# ============================================================================
# Stage 3 — bring up Qdrant + bridge + cli identity
# ============================================================================
log "2/3  bring up — Qdrant + bridge + cli identity"

mkdir -p "$BRIDGE_LOG_DIR" "$QDRANT_DIR"

qdrant_alive() { [[ -f "$QDRANT_PID_FILE" ]] && kill -0 "$(cat "$QDRANT_PID_FILE")" 2>/dev/null; }
bridge_alive() { [[ -f "$BRIDGE_PID_FILE" ]] && kill -0 "$(cat "$BRIDGE_PID_FILE")" 2>/dev/null; }

if qdrant_alive; then
    sub "Qdrant already running (pid $(cat "$QDRANT_PID_FILE")), reusing"
else
    sub "Starting Qdrant in background"
    cd "$QDRANT_DIR"
    nohup qdrant >>"$QDRANT_LOG" 2>&1 &
    echo $! > "$QDRANT_PID_FILE"
    echo "  pid: $(cat "$QDRANT_PID_FILE")"
    echo "  log: $QDRANT_LOG"
fi

sub "Waiting for Qdrant on $QDRANT_URL"
for i in $(seq 1 30); do
    if curl -sf "$QDRANT_URL" >/dev/null 2>&1; then
        echo "  healthy after ${i}s"; break
    fi
    if ! qdrant_alive; then
        echo "  qdrant died — last 30 log lines:" >&2
        tail -30 "$QDRANT_LOG" >&2; exit 1
    fi
    sleep 1
    if [[ "$i" == 30 ]]; then
        echo "  timed out — last 30 log lines:" >&2
        tail -30 "$QDRANT_LOG" >&2; exit 1
    fi
done

if bridge_alive; then
    sub "Bridge already running (pid $(cat "$BRIDGE_PID_FILE")), reusing"
else
    sub "Starting bridge in background"
    cd "$WORKDIR/bridge"
    P2PTB_ADMIN_TOKEN="$(cat "$P2PTB_HOME/admin_token.txt")" \
        nohup "$BRIDGE_BIN" >>"$BRIDGE_LOG" 2>&1 &
    echo $! > "$BRIDGE_PID_FILE"
    echo "  pid: $(cat "$BRIDGE_PID_FILE")"
    echo "  log: $BRIDGE_LOG"
fi

sub "Waiting for bridge on $HEALTH_URL"
for i in $(seq 1 30); do
    if curl -sfk "$HEALTH_URL" >/dev/null 2>&1; then
        echo "  healthy after ${i}s"; break
    fi
    if ! bridge_alive; then
        echo "  bridge died — last 30 log lines:" >&2
        tail -30 "$BRIDGE_LOG" >&2; exit 1
    fi
    sleep 1
    if [[ "$i" == 30 ]]; then
        echo "  timed out — last 30 log lines:" >&2
        tail -30 "$BRIDGE_LOG" >&2; exit 1
    fi
done
echo "  /healthz: $(curl -sk "$HEALTH_URL")"
echo "  /version: $(curl -sk https://localhost:8766/version)"

sub "Starting mnemos (graph memory) broker"
MNEMOS_DIR="$P2PTB_HOME/mnemos"
MNEMOS_SVC="$HARNESS_DIR/services/mnemos"
if [[ -x "$HARNESS_DIR/.venv/bin/python" ]] && [[ -f "$MNEMOS_SVC/broker.py" ]] \
   && "$HARNESS_DIR/.venv/bin/python" -c "import pyoxigraph" >/dev/null 2>&1; then
    mkdir -p "$MNEMOS_DIR"
    if [[ -S "$MNEMOS_DIR/read.sock" ]]; then
        echo "  mnemos already running (socket present)"
    else
        ( cd "$MNEMOS_SVC" && MNEMOS_HOME="$MNEMOS_DIR" \
            nohup "$HARNESS_DIR/.venv/bin/python" broker.py \
            >>"$MNEMOS_DIR/mnemos.log" 2>&1 & echo $! >"$MNEMOS_DIR/mnemos.pid" )
        for _ in $(seq 1 15); do [[ -S "$MNEMOS_DIR/read.sock" ]] && break; sleep 1; done
        if [[ -S "$MNEMOS_DIR/read.sock" ]]; then
            echo "  mnemos graph broker up (socket: $MNEMOS_DIR/read.sock)"
        else
            echo "  WARNING: mnemos broker did not open its socket — see $MNEMOS_DIR/mnemos.log" >&2
        fi
    fi
else
    echo "  skipping mnemos (pyoxigraph unavailable) — agents run on Qdrant memory only"
fi

pause

CLI_USERS_DIR="$HOME/.agent-cli/users"
if [[ ! -d "$CLI_USERS_DIR" ]] || [[ -z "$(ls -A "$CLI_USERS_DIR" 2>/dev/null)" ]]; then
    sub "First-time cli setup: bridge-cli init"
    bridge-cli init --user "${USER:-$(whoami)}"
fi

sub "Running bridge-cli hello (registers with the bridge)"
bridge-cli hello

if [[ "${SKIP_AGENT:-0}" == "1" ]]; then
    cat <<DONE

################################################################
  Swarm is alive (no demo-agent — SKIP_AGENT or no key).
################################################################

  Qdrant   : pid $(cat "$QDRANT_PID_FILE")  |  $QDRANT_URL
  Bridge   : pid $(cat "$BRIDGE_PID_FILE")  |  https://localhost:8766
  CLI keys : $HOME/.agent-cli/

  Things to try:
    curl -sk https://localhost:8766/agents       # who's online
    bridge-cli hello                             # re-register
    spawn scout                                  # add a new agent

  Stop everything:
    kill \$(cat $BRIDGE_PID_FILE) \$(cat $QDRANT_PID_FILE)

  To add the demo-agent later, re-run with OPENAI_API_KEY set.

################################################################
DONE
    if [[ "${NO_CLI:-0}" != "1" ]] && [[ -t 0 ]] && [[ -t 1 ]]; then
        pause
        sub "Launching bridge-cli TUI — q to exit"
        exec bridge-cli tui
    fi
    exit 0
fi

pause

# ============================================================================
# Stage 4 — scaffold + start demo-agent
# ============================================================================
log "3/3  demo-agent — scaffold + --check + start"

cd "$HARNESS_DIR"

if [[ ! -f agent.toml ]]; then
    sub "Scaffolding $AGENT_ID from templates/"
    cp templates/agent.toml.example agent.toml
    python3 - "$AGENT_ID" <<'PY'
import re, sys, pathlib
agent_id = sys.argv[1]
p = pathlib.Path("agent.toml")
src = p.read_text()
src = src.replace('agent_id   = "example-agent"', f'agent_id   = "{agent_id}"')
src = src.replace('state_dir  = "~/.p2ptb/example-agent"',
                  f'state_dir  = "~/.p2ptb/{agent_id}"')
src = re.sub(
    r'\[brain\]\nmodel           = "claude-sonnet-4-6"',
    '[brain]\nprovider        = "openai"\nmodel           = "gpt-4o-mini"',
    src, count=1,
)
src = src.replace('collection = "fidus_memory_prod"',
                  'collection = "demo_memory"')
# Enable the mnemos graph-memory tools (off by default in the template).
src = src.rstrip() + '\n\n[mcp.mnemos]\nenabled = true   # graph memory (mnemos broker)\n'
p.write_text(src)
PY
    echo "  wrote agent.toml (agent_id=${AGENT_ID}, openai, gpt-4o-mini, qdrant=demo_memory)"
else
    sub "agent.toml already exists — leaving alone"
fi

if [[ ! -f CLAUDE.md ]]; then
    cp templates/CLAUDE.md.example CLAUDE.md
    echo "  wrote CLAUDE.md"
fi
if [[ ! -f memory/soul.md ]]; then
    mkdir -p memory
    if [[ -f templates/memory/soul.md.example ]]; then
        cp templates/memory/soul.md.example memory/soul.md
    else
        cat > memory/soul.md <<SOUL
# $AGENT_ID

I am a demo peer on a P2PTB swarm. I exist to prove the install path works.
SOUL
    fi
    echo "  wrote memory/soul.md"
fi

sub "Validating harness install (python3 -m p2ptb_agent --check)"
# shellcheck disable=SC1091
source .venv/bin/activate
PYTHONPATH=. python3 -m p2ptb_agent --check
deactivate

mkdir -p "$AGENT_LOG_DIR"
daemon_alive() { [[ -f "$AGENT_PID_FILE" ]] && kill -0 "$(cat "$AGENT_PID_FILE")" 2>/dev/null; }

# Self-heal stale or wedged registrations so a rerun always converges to
# "daemon alive AND known to the bridge as type=agent". Two failure modes:
#   1. Pre-patch run registered with type=NULL → bridge reports "service"
#      → TUI peer tree hides the agent.
#   2. We DELETEd the agent but the daemon kept its cached trust_token
#      under ~/.p2ptb/identities/<id>/ → daemon spins forever on
#      auth_fail "unknown agent".
# Either way: kill the daemon, DELETE from the bridge, wipe BOTH the
# state dir AND the identity dir, then fall through to a fresh start.
read -r existing_present existing_type < <(curl -sk https://localhost:8766/agents 2>/dev/null \
    | python3 -c "
import json, sys
present = 'no'; t = ''
try:
    d = json.load(sys.stdin)
    a = d.get('agents', d) if isinstance(d, dict) else d
    for x in a:
        if x.get('agent_id') == '${AGENT_ID}':
            present = 'yes'
            t = x.get('type', '') or x.get('declared_type', '')
            break
except Exception:
    pass
print(present, t)" 2>/dev/null || echo "no ")

needs_reset=0
reset_reason=""
if [[ "$existing_present" == "yes" ]] && [[ -n "$existing_type" ]] && [[ "$existing_type" != "agent" ]]; then
    needs_reset=1
    reset_reason="registered as type=$existing_type"
elif [[ "$existing_present" == "no" ]] && daemon_alive; then
    needs_reset=1
    reset_reason="daemon alive but bridge doesn't know it (auth-wedged)"
fi

if [[ "$needs_reset" == 1 ]]; then
    sub "Resetting $AGENT_ID — $reset_reason"
    if daemon_alive; then
        kill "$(cat "$AGENT_PID_FILE")" 2>/dev/null || true
        for i in 1 2 3 4 5; do
            daemon_alive || break
            sleep 1
        done
        daemon_alive && kill -9 "$(cat "$AGENT_PID_FILE")" 2>/dev/null || true
        rm -f "$AGENT_PID_FILE"
    fi
    curl -sk -X DELETE \
        -H "Authorization: Bearer $(cat "$P2PTB_HOME/admin_token.txt")" \
        "https://localhost:8766/admin/agents/$AGENT_ID" >/dev/null 2>&1 || true
    rm -rf "$AGENT_LOG_DIR"
    rm -rf "$P2PTB_HOME/identities/$AGENT_ID"
    mkdir -p "$AGENT_LOG_DIR"
    echo "  registration cleared; daemon will re-register fresh"
fi

if daemon_alive; then
    sub "Daemon already running (pid $(cat "$AGENT_PID_FILE")), reusing"
else
    sub "Starting $AGENT_ID daemon in background"
    cd "$HARNESS_DIR"
    # shellcheck disable=SC1091
    source .venv/bin/activate
    PYTHONPATH=. OPENAI_API_KEY="$OPENAI_API_KEY" \
        nohup python3 -m p2ptb_agent >>"$AGENT_LOG" 2>&1 &
    echo $! > "$AGENT_PID_FILE"
    deactivate
    echo "  pid: $(cat "$AGENT_PID_FILE")"
    echo "  log: $AGENT_LOG"

    sub "Waiting for $AGENT_ID to register on the bridge"
    for i in $(seq 1 30); do
        if curl -sk https://localhost:8766/agents \
            | python3 -c "import json,sys; d=json.load(sys.stdin); a=d.get('agents',d) if isinstance(d,dict) else d; sys.exit(0 if any(x.get('agent_id')=='${AGENT_ID}' and x.get('state')=='online' for x in a) else 1)" \
            2>/dev/null; then
            echo "  registered after ${i}s"; break
        fi
        if ! daemon_alive; then
            echo "  daemon died — last 30 log lines:" >&2
            tail -30 "$AGENT_LOG" >&2; exit 1
        fi
        sleep 1
        if [[ "$i" == 30 ]]; then
            echo "  timed out — last 30 log lines:" >&2
            tail -30 "$AGENT_LOG" >&2; exit 1
        fi
    done

    echo
    echo "  /agents now reports:"
    curl -sk https://localhost:8766/agents | python3 -m json.tool
fi

cat <<DONE

################################################################
  Swarm is fully up.
################################################################

  Qdrant       : pid $(cat "$QDRANT_PID_FILE")  |  $QDRANT_URL
  Bridge       : pid $(cat "$BRIDGE_PID_FILE")  |  https://localhost:8766
  ${AGENT_ID}  : pid $(cat "$AGENT_PID_FILE")  |  $AGENT_LOG

  Things to try:
    curl -sk https://localhost:8766/agents          # who's online
    bridge-cli send ${AGENT_ID} '{"intent":"ping"}'
    spawn scout                                     # add another agent

  Stop everything:
    kill \$(cat $BRIDGE_PID_FILE) \$(cat $QDRANT_PID_FILE) \$(cat $AGENT_PID_FILE)

  Tail logs:
    tail -f $BRIDGE_LOG
    tail -f $QDRANT_LOG
    tail -f $AGENT_LOG

################################################################
DONE

if [[ "${NO_CLI:-0}" != "1" ]] && [[ -t 0 ]] && [[ -t 1 ]]; then
    pause
    sub "Launching bridge-cli TUI — q to exit"
    # First-startup intro: only on the very first successful install. Touch
    # a marker after firing so reruns of we1re.sh don't re-prompt the agent.
    # The detached subshell survives our exec into bridge-cli.
    INTRO_MARKER="$P2PTB_HOME/.cli-introduced"
    if [[ ! -f "$INTRO_MARKER" ]]; then
        (
            sleep 3
            if bridge-cli send "$AGENT_ID" \
                '{"intent":"inform","content":"Your operator has just opened the CLI for the first time on a fresh P2PTB swarm. Give them a short friendly self-status announcement in one paragraph: your name, the model you run, the tools you have available, and confirm your memory backend is reachable. Sign off by inviting them to chat."}' \
                >/dev/null 2>&1; then
                touch "$INTRO_MARKER"
            fi
        ) &
    fi
    exec bridge-cli tui
elif [[ "${NO_CLI:-0}" != "1" ]]; then
    echo
    echo "  stdout/stdin isn't a TTY — skipping auto-launch."
    echo "  Open the TUI yourself with: bridge-cli tui"
fi
