#!/usr/bin/env bash
# ============================================================
# seed-neo4j.sh — Neo4j schema + root-node seeding
# Applies the schema (constraints, indexes, vector indexes) and
# MERGEs the per-account root :LocalBusiness and owner :AdminUser
# nodes. Account directory creation, Claude Code settings, hooks
# wiring, and specialist templates live in `setup-account.sh`.
# This script does no filesystem setup beyond reading what
# setup-account.sh wrote.
# ============================================================

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
NEO4J_DIR="$PROJECT_DIR/neo4j"
# Accounts live at {installDir}/data/accounts/ — outside the platform/ wipe
# zone. resolve_and_sweep_account_dir reads ACCOUNTS_DIR from env.
INSTALL_DIR="$(dirname "$PROJECT_DIR")"
ACCOUNTS_DIR="$INSTALL_DIR/data/accounts"

# NEO4J_URI is hard-required. The previous default
# `bolt://localhost:7687` would silently route the seed to the wrong Neo4j on
# any brand-dedicated install — masking every other defect downstream.
if [ -z "${NEO4J_URI:-}" ]; then
  echo "Error: NEO4J_URI required (no default)" >&2
  exit 1
fi
NEO4J_USER="${NEO4J_USER:-neo4j}"

# Read password from the file created during setup
NEO4J_PASSWORD_FILE="$PROJECT_DIR/config/.neo4j-password"
if [ -n "${NEO4J_PASSWORD:-}" ]; then
  : # Explicit env var takes precedence
elif [ -f "$NEO4J_PASSWORD_FILE" ]; then
  NEO4J_PASSWORD=$(cat "$NEO4J_PASSWORD_FILE")
else
  echo "Error: Neo4j password not found."
  echo "  Expected at: $NEO4J_PASSWORD_FILE"
  echo "  Or set NEO4J_PASSWORD environment variable."
  exit 1
fi

CYPHER_SHELL="cypher-shell"

# ------------------------------------------------------------------
# 1. Resolve the account dir (must already exist; setup-account.sh
#    is the creator). The resolver is idempotent — calling it here
#    re-finds the same ACCOUNT_DIR / ACCOUNT_ID setup-account.sh
#    minted.
# ------------------------------------------------------------------
# shellcheck source=lib/resolve-account-dir.sh
. "$SCRIPT_DIR/lib/resolve-account-dir.sh"
# shellcheck source=lib/read-brand-json.sh
. "$SCRIPT_DIR/lib/read-brand-json.sh"
_RESOLVER_CONFIG_DIR_NAME=".maxy"
read_brand_json_key "$PROJECT_DIR/config/brand.json" "configDir"
[ -n "$BRAND_JSON_VALUE" ] && _RESOLVER_CONFIG_DIR_NAME="$BRAND_JSON_VALUE"
USERS_FILE="$HOME/$_RESOLVER_CONFIG_DIR_NAME/users.json" resolve_and_sweep_account_dir

# ------------------------------------------------------------------
# 2. Apply Neo4j schema (constraints + indexes only)
# ------------------------------------------------------------------
if ! command -v "$CYPHER_SHELL" &> /dev/null; then
    echo "Error: cypher-shell not found. Install Neo4j or add cypher-shell to PATH."
    exit 1
fi

echo "==> Connecting to Neo4j at $NEO4J_URI as $NEO4J_USER"

# Vector index dimensions — configurable at install time via --embed-model.
# The installer sets EMBED_DIMENSIONS in the environment; default 768 (nomic-embed-text).
EMBED_DIMENSIONS="${EMBED_DIMENSIONS:-768}"
echo "==> Applying schema (constraints, indexes, vector indexes — ${EMBED_DIMENSIONS} dims)..."

SCHEMA_CYPHER=$(sed "s/\`vector\.dimensions\`: 768/\`vector.dimensions\`: ${EMBED_DIMENSIONS}/g" "$NEO4J_DIR/schema.cypher")

# Verify the substitution produced the expected dimension value
if [ "$EMBED_DIMENSIONS" != "768" ] && ! echo "$SCHEMA_CYPHER" | grep -q "\`vector.dimensions\`: ${EMBED_DIMENSIONS}"; then
  echo "WARNING: Expected dimension ${EMBED_DIMENSIONS} not found in schema output — indexes may use default 768"
fi

echo "$SCHEMA_CYPHER" | "$CYPHER_SHELL" -u "$NEO4J_USER" -p "$NEO4J_PASSWORD" -a "$NEO4J_URI"

# ------------------------------------------------------------------
# 3. Bootstrap root :LocalBusiness for the account
# ------------------------------------------------------------------
# Every node in the graph hierarchy (Project, Task, Person, Organisation,
# KnowledgeDocument) anchors to a :LocalBusiness via accountId. Without a
# root LocalBusiness, the recorder's "if no parent, do not write" rule
# fires correctly and the first write attempt is dropped. Stamp one
# LocalBusiness per account at seed time so the hierarchy has a root from
# install onward. Idempotent via MERGE on the (accountId) unique key.
#
# businessType is the bare slug (e.g. "estate-agent"). resolve-active-vertical.ts
# prepends "schema-" when reading back, so the stored value must NOT carry
# the prefix. brand.json#vertical holds the file basename ("schema-estate-agent")
# and is stripped here.
#
# ON MATCH coalesces businessType: an operator override on the LocalBusiness
# wins over the brand default; only NULL-businessType nodes get the brand
# default filled in on re-seed. createdAt is set ON CREATE only, so re-runs
# preserve the original timestamp.

BRAND_JSON="$PROJECT_DIR/config/brand.json"
read_brand_json_key "$BRAND_JSON" "vertical"
LOCALBUSINESS_BUSINESS_TYPE=$(derive_business_type "$BRAND_JSON_VALUE")
LOCALBUSINESS_BT_CYPHER=$(business_type_cypher "$LOCALBUSINESS_BUSINESS_TYPE")
LOCALBUSINESS_BT_LOG="${LOCALBUSINESS_BUSINESS_TYPE:-none}"

# Pre-count distinguishes created vs already-exists for the log line.
# Simpler than an in-cypher transient flag; the extra round-trip is
# sub-millisecond on a local Neo4j.
if ! LOCALBUSINESS_EXISTING_RAW=$("$CYPHER_SHELL" -u "$NEO4J_USER" -p "$NEO4J_PASSWORD" -a "$NEO4J_URI" \
    --format plain \
    "MATCH (lb:LocalBusiness {accountId: '$ACCOUNT_ID'}) RETURN count(lb)" 2>&1); then
  LOCALBUSINESS_REASON=$(echo "$LOCALBUSINESS_EXISTING_RAW" | tr '\n' '|' | head -c 500)
  echo "  [install-invariant] localbusiness-bootstrap accountId=${ACCOUNT_ID:0:8} result=failed reason=$LOCALBUSINESS_REASON" >&2
  exit 1
fi
LOCALBUSINESS_EXISTING=$(echo "$LOCALBUSINESS_EXISTING_RAW" | tail -1 | tr -d ' \r')

# $ACCOUNT_ID is a UUID minted by the resolver; $LOCALBUSINESS_BT_CYPHER is
# either a single-quoted slug from a closed enum (brands/*/brand.json) or the
# bare keyword null. No operator input is interpolated.
if ! LOCALBUSINESS_OUT=$("$CYPHER_SHELL" -u "$NEO4J_USER" -p "$NEO4J_PASSWORD" -a "$NEO4J_URI" 2>&1 << CYPHER_EOF
MERGE (lb:LocalBusiness {accountId: '$ACCOUNT_ID'})
  ON CREATE SET lb.createdBySource = 'installer',
                lb.createdAt = datetime(),
                lb.businessType = $LOCALBUSINESS_BT_CYPHER
  ON MATCH SET lb.businessType = coalesce(lb.businessType, $LOCALBUSINESS_BT_CYPHER)
RETURN lb.accountId;
CYPHER_EOF
); then
  LOCALBUSINESS_REASON=$(echo "$LOCALBUSINESS_OUT" | tr '\n' '|' | head -c 500)
  echo "  [install-invariant] localbusiness-bootstrap accountId=${ACCOUNT_ID:0:8} businessType=$LOCALBUSINESS_BT_LOG result=failed reason=$LOCALBUSINESS_REASON" >&2
  exit 1
fi

if [ "$LOCALBUSINESS_EXISTING" = "0" ]; then
  LOCALBUSINESS_RESULT="created"
else
  LOCALBUSINESS_RESULT="already-exists"
fi
echo "  [install-invariant] localbusiness-bootstrap accountId=${ACCOUNT_ID:0:8} businessType=$LOCALBUSINESS_BT_LOG result=$LOCALBUSINESS_RESULT"

# ------------------------------------------------------------------
# 4. Create AdminUser node + ADMIN_OF relationship
# ------------------------------------------------------------------
# Resolve owner userId from users.json (set-pin POST creates it).
# Same brand-aware resolution as setup-account.sh.
CONFIG_DIR="$PROJECT_DIR/config"
_CONFIG_DIR_NAME=".maxy"
read_brand_json_key "$CONFIG_DIR/brand.json" "configDir"
[ -n "$BRAND_JSON_VALUE" ] && _CONFIG_DIR_NAME="$BRAND_JSON_VALUE"
USERS_FILE="$HOME/$_CONFIG_DIR_NAME/users.json"

USER_ID=""
if [ -f "$USERS_FILE" ]; then
  USER_ID=$(python3 -c "import json; print(json.load(open('$USERS_FILE'))[0]['userId'])" 2>/dev/null || true)
fi

if [ -n "$USER_ID" ]; then
  # Escape single quotes for Cypher string interpolation (e.g. O'Brien → O''Brien).
  USER_NAME=$(python3 -c "import json; n=json.load(open('$USERS_FILE'))[0]['name']; print(n.replace(\"'\",\"''\"))" 2>/dev/null || echo "Owner")
  CREATED_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ)

  echo "==> Creating AdminUser node for userId=$USER_ID"
  # Stamp accountId + role='owner' at seed so the graph-write gate's
  # account-scoped MATCH (au:AdminUser {accountId: $accountId}) succeeds on the
  # first user-domain write. The ON MATCH branch COALESCEs to preserve any
  # existing non-null value.
  "$CYPHER_SHELL" -u "$NEO4J_USER" -p "$NEO4J_PASSWORD" -a "$NEO4J_URI" << CYPHER_EOF
MERGE (au:AdminUser {userId: '$USER_ID'})
ON CREATE SET au.accountId = '$ACCOUNT_ID',
              au.role = 'owner',
              au.name = '$USER_NAME',
              au.createdAt = '$CREATED_AT'
ON MATCH SET au.accountId = COALESCE(au.accountId, '$ACCOUNT_ID'),
             au.role = COALESCE(au.role, 'owner'),
             au.name = '$USER_NAME'
WITH au
MATCH (b:LocalBusiness {accountId: '$ACCOUNT_ID'})
MERGE (au)-[r:ADMIN_OF]->(b)
ON CREATE SET r.role = 'owner', r.grantedAt = '$CREATED_AT'
RETURN au.userId, b.accountId;
CYPHER_EOF
  echo "  [seed] AdminUser stamped userId=${USER_ID:0:8} accountId=${ACCOUNT_ID:0:8} role=owner"
else
  echo "  No users.json found — skipping AdminUser seed (set-pin POST creates it)"
fi

echo "  Done."
