#!/usr/bin/env bash
# ============================================================================
# beagle-store-code-on — build and serve a native code corpus for a directory.
#   beagle-store-code-on <dir> [port] --space-id <id> [--src <subdir>]
# --src scopes ingest+corpus to the SOURCE subtree (default: <dir>) while .store/
# and .codex/config.toml stay at <dir> — repos whose tree also holds build artifacts /
# vendored copies (gjoa: gitignored engine/ mirrors src/) must not ingest those.
# Does the whole incantation in one shot:
#   1. ingest <dir> -> .store/code.log (native Store transaction log)
#   2. boot the native server on that log
#   3. wire the public Beagle Store data MCP to that SpaceId
# Graph-control authoring is a separate sealed service and is not enabled here.
# Turn OFF: beagle-store-code-off <dir>  (stops the server; removes the Codex MCP entry).
# ============================================================================
set -euo pipefail
HERE="$(cd "$(dirname "$0")/.." && pwd)"          # store repo root
BEAGLE_HOME="${BEAGLE_HOME:-$HOME/code/beagle/main}"
BEAGLE_HOME="$(cd "$BEAGLE_HOME" && pwd)"
DIR="${1:?usage: beagle-store-code-on <dir> [port] --space-id <id> [--src <subdir>]}"; DIR="$(cd "$DIR" && pwd)"; shift
PORT="" SRC="$DIR" SPACE_ID=""
while [ $# -gt 0 ]; do
  case "$1" in
    --src) SRC="${2:?--src needs a dir}"; shift 2;;
    --space-id) SPACE_ID="${2:?--space-id needs an id}"; shift 2;;
    --*) echo "beagle-store-code-on: unknown option $1" >&2; exit 2;;
    *)
      if [[ -n "$PORT" ]]; then
        echo "beagle-store-code-on: unexpected argument $1" >&2
        exit 2
      fi
      PORT="$1"; shift;;
  esac
done
[[ -n "$SPACE_ID" ]] || {
  echo "beagle-store-code-on: --space-id is required and must be nonempty" >&2
  exit 2
}
SRC="$(cd "$SRC" && pwd)"
PORT="${PORT:-$(( (RANDOM % 20000) + 30000 ))}"
CODE_LOG="$DIR/.store/code.log"
mkdir -p "$DIR/.store"

echo "beagle-store-code-on: $DIR (space $SPACE_ID; port $PORT; src $SRC)"

native_status_line() {
  BEAGLE_STORE_SERVER_PORT="$PORT" BEAGLE_STORE_SPACE_ID="$SPACE_ID" BEAGLE_STORE_LOG="$CODE_LOG" \
    timeout 5 "$HERE/../bin/beagle" store status 2>/dev/null
}

verify_native_server() {
  local status
  status="$(native_status_line || true)"
  if [[ ! "$status" =~ ^up\|[0-9]+\|[0-9]+\|ready\|jvm$ ]]; then
    echo "        native :rpc/status check FAILED: ${status:-no native response}" >&2
    echo "        expected SpaceId $SPACE_ID on :$PORT" >&2
    return 1
  fi
  printf '%s\n' "$status"
}

# 1. ingest the code into propositions (deterministic, no LLM) — recurse for any live Beagle source.
#    The parsed AST is target-independent, so ingest is too.
#    beagle-store-ingest-code is non-recursive, so we find + feed it every Beagle file here.
#
#    EXCLUDE docs/private/, vendored copies, tests, and fuzz repros: none are
#    members of the authoring corpus.
if pgrep -f "server.clj serve [0-9]+ $CODE_LOG( |$)" >/dev/null 2>&1; then
  echo "beagle-store-code-on: a server already owns $CODE_LOG; run beagle-store-code-off first" >&2
  exit 1
fi
if ss -ltn 2>/dev/null | grep -q ":$PORT "; then
  echo "beagle-store-code-on: port $PORT is already in use" >&2
  exit 1
fi

echo "  [1/3] ingesting code -> native Store transaction log …"
mapfile -t SRCS < <(find "$SRC" -regextype posix-extended -regex '.*\.b(clj|js|nix|gl)$' -not -path '*/.store/*' -not -path '*/docs/private/*' -not -path '*/test/*' -not -path '*/tests/*' | sort)
[ "${#SRCS[@]}" -eq 0 ] && { echo "        no live Beagle source under $SRC"; exit 1; }
( cd "$HERE" && BEAGLE_HOME="$BEAGLE_HOME" bin/beagle-store-ingest-code "${SRCS[@]}" \
    --root "$SRC" --out "$CODE_LOG" --space-id "$SPACE_ID" ) \
  && echo "        ${#SRCS[@]} module transactions -> $CODE_LOG"

# 1b. resolve AST -> reference graph (corpus) for REASONING. code.log is the editing
#     AST (tree); the corpus is the resolved who-calls/blast-radius GRAPH (callgraph needs it).
CORPUS="$DIR/.store/corpus.facts"
echo "  [1b]  resolving -> reference graph (for who-calls/blast-radius) …"
( cd "$HERE/codegraph" && BEAGLE_HOME="$BEAGLE_HOME" bin/emit-corpus "$SRC" "$CORPUS" >/dev/null 2>&1 ) \
  && echo "        corpus: $(wc -l <"$CORPUS" 2>/dev/null) reference rows -> $CORPUS" \
  || echo "        (reference corpus skipped — native AST corpus is still available)"

# 2. boot the native server (detached — survives this shell)
echo "  [2/3] booting native server on :$PORT …"
( cd "$HERE" && exec env -u BEAGLE_STORE_TELEMETRY_LOG \
  BEAGLE_HOME="$BEAGLE_HOME" \
  setsid nohup bin/beagle-store-server serve "$PORT" "$CODE_LOG" "$SPACE_ID" \
    >"$DIR/.store/server-$PORT.log" 2>&1 ) &
SERVER_PID=$!
for _ in $(seq 1 2400); do
  sleep 0.5
  grep -q "listening on" "$DIR/.store/server-$PORT.log" 2>/dev/null && break
  if ! kill -0 "$SERVER_PID" 2>/dev/null; then
    echo "        FAILED to boot — server exited; see $DIR/.store/server-$PORT.log" >&2
    tail -n 8 "$DIR/.store/server-$PORT.log" >&2 || true
    exit 1
  fi
done
if grep -q "listening on" "$DIR/.store/server-$PORT.log" 2>/dev/null; then
  if NATIVE_STATUS="$(verify_native_server)"; then
    echo "        server up ($NATIVE_STATUS)"
  else
    kill "$SERVER_PID" 2>/dev/null || true
    exit 1
  fi
else
  echo "        FAILED to boot — see $DIR/.store/server-$PORT.log"
  kill "$SERVER_PID" 2>/dev/null || true
  exit 1
fi

# 3. write .codex/config.toml [mcp_servers.beagle-store]. Merge without
#    clobbering unrelated servers or projects.
echo "  [3/3] wiring $DIR/.codex/config.toml …"
BEAGLE_STORE_SERVER_JSON=$(cat <<JSON
{
  "command": "$HERE/bin/beagle-store-mcp",
  "args": [],
  "env": {
    "BEAGLE_STORE_SPACE_ID": "$SPACE_ID",
    "BEAGLE_STORE_SERVER_PORT": "$PORT",
    "BEAGLE_STORE_SERVER_READ_TIMEOUT_MS": "180000",
    "BEAGLE_STORE_LOG": "$CODE_LOG"
  }
}
JSON
)
"$HERE/bin/beagle-store-code-wire" on "$DIR" "$BEAGLE_STORE_SERVER_JSON"

# The transport wiring and server boot are separate steps. Prove the
# user-visible native stack is complete before reporting success. Level 3 stays
# reserved for the separate graph-control plane.
STACK_STATUS="$("$HERE/bin/beagle-store-code-status" "$DIR")"
if [[ ! "$STACK_STATUS" =~ ^level=2\  ]] || \
   [[ "$STACK_STATUS" != *"mcp=present space=$SPACE_ID port=$PORT server=alive"* ]] || \
   ! grep -q '^\[mcp_servers\.store\]$' "$DIR/.codex/config.toml"; then
  echo "beagle-store-code-on: FAILED final native stack postcondition: $STACK_STATUS" >&2
  exit 1
fi

cat <<EOF

=== NATIVE CODE CORPUS ON ===
Store transaction log + server: $NATIVE_STATUS
The public Beagle Store data MCP is wired to SpaceId $SPACE_ID.
Graph-control authoring is not enabled here; that is the separate N2.6b seam.
Reason over the code graph (blast-radius / who-calls), any time:
  bb -cp $HERE/out $HERE/out/callgraph.clj $CORPUS
Or race it vs grep on any function:
  $HERE/../after-text/experiments/authoring-bakeoff/race/reason-race.sh <fn>
Turn OFF: $HERE/bin/beagle-store-code-off $DIR
EOF
