#!/usr/bin/env bash
# Structural smoke test for ruflo-agentdb plugin v0.3.0.
# Per ADR-0001 §6 Verification: 10 numbered checks against the documented
# CLI MCP surface plus three documentation invariants.
#
# This script verifies plugin structural soundness and source-level
# contracts. Live MCP-tool exercise (against a running daemon with the
# bridge wired) is layered on top via the optional --live flag, but the
# default invocation is offline-safe and CI-friendly.
set -u
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
REPO_ROOT="$(cd "$ROOT/../.." && pwd)"
LIVE=0
for arg in "$@"; do
  case "$arg" in --live) LIVE=1 ;; esac
done

PASS=0
FAIL=0

step() { printf "→ %s ... " "$1"; }
ok()   { printf "PASS\n"; PASS=$((PASS+1)); }
bad()  { printf "FAIL: %s\n" "$1"; FAIL=$((FAIL+1)); }

# Documentation invariants (ADR-0001 §Verification)
step "INV1: no '19 controllers' string in plugin docs (excluding ADRs which document history)"
# ADR files inside docs/adrs/ are allowed to reference "19 controllers" because
# they document the change. The invariant is on user-facing docs only.
hits=$(grep -rE '19 (AgentDB )?[Cc]ontrollers|all 19 controllers' "$ROOT" \
   --include='*.md' --include='*.json' \
   --exclude-dir='adrs' 2>/dev/null || true)
if [[ -n "$hits" ]]; then
  bad "stale '19 controllers' phrasing still present in user-facing docs:"
  echo "$hits" | sed 's/^/    /'
else
  ok
fi

step "INV2: RaBitQ workflow documented in vector-search SKILL"
grep -q "embeddings_rabitq_build" "$ROOT/skills/vector-search/SKILL.md" \
  && grep -q "embeddings_rabitq_search" "$ROOT/skills/vector-search/SKILL.md" \
  && grep -q "embeddings_rabitq_status" "$ROOT/skills/vector-search/SKILL.md" \
  && ok || bad "RaBitQ trio not all documented"

step "INV3: README has the Namespace convention section"
grep -q "## Namespace convention" "$ROOT/README.md" \
  && grep -q "Reserved namespaces" "$ROOT/README.md" \
  && ok || bad "Namespace convention section missing"

# 1. Plugin version + new keywords
step "1. plugin.json declares version 0.3.0 with rabitq + namespace-convention keywords"
v=$(grep -E '"version"[[:space:]]*:' "$ROOT/.claude-plugin/plugin.json" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
if [[ "$v" != "0.3.0" ]]; then
  bad "expected 0.3.0, got '$v'"
else
  miss=""
  for k in rabitq quantization namespace-convention controller-bridge; do
    grep -q "\"$k\"" "$ROOT/.claude-plugin/plugin.json" || miss="$miss $k"
  done
  [[ -z "$miss" ]] && ok || bad "missing keywords:$miss"
fi

# 2. Controller-count claims defer to runtime
step "2. README/agent/command/skill defer controller count to runtime tool"
miss=""
for f in "$ROOT/README.md" "$ROOT/agents/agentdb-specialist.md" "$ROOT/commands/agentdb.md" "$ROOT/skills/agentdb-query/SKILL.md"; do
  grep -q "agentdb_controllers" "$f" || miss="$miss $(basename $f)"
done
[[ -z "$miss" ]] && ok || bad "missing agentdb_controllers reference:$miss"

# 3. All 15 agentdb_* tool names are documented somewhere in the plugin
step "3. all 15 agentdb_* tool names referenced in plugin docs"
miss=""
for t in agentdb_health agentdb_controllers agentdb_pattern-store agentdb_pattern-search agentdb_feedback agentdb_causal-edge agentdb_route agentdb_session-start agentdb_session-end agentdb_hierarchical-store agentdb_hierarchical-recall agentdb_consolidate agentdb_batch agentdb_context-synthesize agentdb_semantic-route; do
  grep -rq "$t" "$ROOT" --include='*.md' || miss="$miss $t"
done
[[ -z "$miss" ]] && ok || bad "undocumented:$miss"

# 4. All 10 embeddings_* tools documented
step "4. all 10 embeddings_* tool names referenced in plugin docs"
miss=""
for t in embeddings_init embeddings_generate embeddings_compare embeddings_search embeddings_neural embeddings_hyperbolic embeddings_status embeddings_rabitq_build embeddings_rabitq_search embeddings_rabitq_status; do
  grep -rq "$t" "$ROOT" --include='*.md' || miss="$miss $t"
done
[[ -z "$miss" ]] && ok || bad "undocumented:$miss"

# 5. The 3 ruvllm_hnsw_* tools documented + the ~11 cap is acknowledged
step "5. ruvllm_hnsw_* tools documented and ~11 cap acknowledged"
miss=""
for t in ruvllm_hnsw_create ruvllm_hnsw_add ruvllm_hnsw_route; do
  grep -rq "$t" "$ROOT" --include='*.md' || miss="$miss $t"
done
if [[ -n "$miss" ]]; then
  bad "undocumented:$miss"
elif ! grep -rq "11 patterns\|≤11" "$ROOT/skills/vector-search/SKILL.md" "$ROOT/README.md" "$ROOT/agents/agentdb-specialist.md"; then
  bad "WASM cap (~11 patterns) not acknowledged"
else
  ok
fi

# 6. RaBitQ workflow has a 5-step recipe + the rerank caveat
step "6. RaBitQ section has the 5-step recipe + rerank caveat"
F="$ROOT/skills/vector-search/SKILL.md"
if grep -q "Quantized search" "$F" \
   && grep -q "rerank" "$F" \
   && grep -qE "embeddings_rabitq_build" "$F" \
   && grep -qE "embeddings_rabitq_search" "$F" \
   && grep -qE "embeddings_rabitq_status" "$F"; then
  ok
else
  bad "RaBitQ section incomplete (missing recipe or rerank caveat)"
fi

# 7. Pattern-store fallback is source-inspectable (no env var gates this — see ADR §6 caveat)
step "7. memory-store-fallback path is source-inspectable in agentdb-tools.ts"
TOOLS="$REPO_ROOT/v3/@claude-flow/cli/src/mcp-tools/agentdb-tools.ts"
if [[ -f "$TOOLS" ]] \
   && grep -q "memory-store-fallback" "$TOOLS" \
   && grep -q "ReasoningBank controller registry unavailable" "$TOOLS"; then
  ok
else
  bad "fallback string not present in agentdb-tools.ts (or file moved)"
fi

# 8. agentdb_hierarchical-store rejects an unknown tier
step "8. tier validator rejects unknown tier values"
TOOLS="$REPO_ROOT/v3/@claude-flow/cli/src/mcp-tools/agentdb-tools.ts"
if grep -qE "(working|episodic|semantic).*tier|tier.*(working|episodic|semantic)" "$TOOLS"; then
  ok
else
  bad "tier validator pattern not found in agentdb-tools.ts"
fi

# 9. Batch size is bounded
step "9. agentdb_batch enforces MAX_BATCH_SIZE = 500"
TOOLS="$REPO_ROOT/v3/@claude-flow/cli/src/mcp-tools/agentdb-tools.ts"
if grep -qE "MAX_BATCH_SIZE\s*=\s*500" "$TOOLS"; then
  ok
else
  bad "MAX_BATCH_SIZE = 500 not present (or moved)"
fi

# 10. Namespace convention guardrails: reserved list + colon rule + length cap
step "10. namespace guardrails documented (reserved list + colon rule + length cap)"
F="$ROOT/README.md"
miss=""
grep -q "claude-memories" "$F" || miss="$miss reserved-list"
grep -qE "SHOULD NOT contain.+:.+colon|colon.+delimiter" "$F" || \
  grep -qE 'NOT contain `:`' "$F" || miss="$miss colon-rule"
grep -qE "200 chars|≤200" "$F" || miss="$miss length-cap"
[[ -z "$miss" ]] && ok || bad "guardrail set incomplete:$miss"

# 11. Auto-memory bridge mechanics documented (Claude Code populates claude-memories)
step "11. auto-memory bridge mechanics documented (memory_import_claude + auto-memory-hook.mjs)"
F="$ROOT/README.md"
if grep -q "How Claude Code populates AgentDB" "$F" \
   && grep -q "memory_import_claude" "$F" \
   && grep -q "auto-memory-hook.mjs" "$F" \
   && grep -q "memory_bridge_status" "$F" \
   && grep -q "memory_search_unified" "$F"; then
  ok
else
  bad "auto-memory bridge section missing pieces (header, import tool, hook script, bridge status, unified search)"
fi

# 12. Hook → namespace integration table
step "12. hook integration table maps hook → namespace it writes to"
F="$ROOT/README.md"
if grep -q "Hook integration convention" "$F" \
   && grep -q "SessionStart" "$F" \
   && grep -q "post-task --train-neural" "$F" \
   && grep -qE "ReasoningBank.*pattern|pattern.*ReasoningBank" "$F"; then
  ok
else
  bad "hook integration table missing (SessionStart, post-task --train-neural, ReasoningBank → pattern)"
fi

# 13. Pluralization gotcha (pattern vs patterns)
step "13. pluralization gotcha documented (pattern vs patterns)"
F="$ROOT/README.md"
if grep -qiE "pluraliz.*gotcha|pattern.*plural|plural.*pattern" "$F" \
   && grep -q '`pattern`' "$F" \
   && grep -q '`patterns`' "$F"; then
  ok
else
  bad "pluralization callout missing (must contrast pattern vs patterns)"
fi

# Live mode (optional, requires daemon)
if [[ "$LIVE" == "1" ]]; then
  step "L1 (live): agentdb_health responds (requires running daemon)"
  if command -v claude-flow >/dev/null 2>&1; then
    if claude-flow mcp call agentdb_health 2>/dev/null | grep -q '"available"'; then
      ok
    else
      bad "agentdb_health did not return an 'available' field"
    fi
  else
    bad "claude-flow CLI not on PATH (skipping --live checks)"
  fi
fi

printf "\n%s passed, %s failed\n" "$PASS" "$FAIL"
[[ $FAIL -eq 0 ]] || exit 1
