#!/usr/bin/env bash
# ============================================================
# migrate-import.sh — Import a migration bundle into a Maxy install
#
# Reads a migration bundle (produced by taskmaster-export.sh) and
# populates a Maxy/Real Agent installation: Neo4j contacts, memory
# files, media, conversations, and access PINs.
#
# Idempotent: running twice does not create duplicate contacts
# (MERGE on telephone) or corrupt existing data (file overwrites
# are safe for migration content).
#
# Usage:
#   bash migrate-import.sh <bundle-dir> <install-dir> [--account <uuid>]
#
# Arguments:
#   bundle-dir  — Migration bundle directory (from taskmaster-export.sh)
#   install-dir — Maxy installation root (contains data/accounts/ and platform/)
#   --account   — (Optional) Target account UUID. Required if multiple accounts exist.
#
# Example:
#   bash migrate-import.sh ./bundle ~/maxy
#   bash migrate-import.sh ./bundle ~/maxy --account a1b2c3d4-...
# ============================================================

set -euo pipefail

# ------------------------------------------------------------------
# Arguments
# ------------------------------------------------------------------
if [ $# -lt 2 ]; then
  echo "[import] ERROR: Usage: migrate-import.sh <bundle-dir> <install-dir> [--account <uuid>]"
  exit 2
fi

BUNDLE_DIR="$1"
INSTALL_DIR="$2"
ACCOUNT_FLAG=""

shift 2
while [ $# -gt 0 ]; do
  case "$1" in
    --account)
      ACCOUNT_FLAG="$2"
      shift 2
      ;;
    *)
      echo "[import] ERROR: Unknown argument: $1"
      exit 2
      ;;
  esac
done

# Validate bundle
if [ ! -f "$BUNDLE_DIR/manifest.json" ]; then
  echo "[import] ERROR: manifest.json not found in $BUNDLE_DIR"
  echo "[import] ERROR: Is this a valid migration bundle?"
  exit 1
fi

# Display manifest
echo "[import] Bundle: $BUNDLE_DIR"
python3 -c "
import json
m = json.load(open('$BUNDLE_DIR/manifest.json'))
print(f'[import]   Source:    {m.get(\"source\",\"?\")} v{m.get(\"version\",\"?\")}')
print(f'[import]   Customer: {m.get(\"customer\",\"?\")}')
print(f'[import]   Exported: {m.get(\"exportedAt\",\"?\")}')
print(f'[import]   Hostname: {m.get(\"hostname\",\"?\")}')
"

# ------------------------------------------------------------------
# Discover account
# ------------------------------------------------------------------
ACCOUNTS_DIR="$INSTALL_DIR/data/accounts"

if [ ! -d "$ACCOUNTS_DIR" ]; then
  echo "[import] ERROR: No accounts directory at $ACCOUNTS_DIR"
  echo "[import] ERROR: Is $INSTALL_DIR a Maxy installation?"
  exit 1
fi

if [ -n "$ACCOUNT_FLAG" ]; then
  # Explicit account UUID provided
  ACCOUNT_DIR="$ACCOUNTS_DIR/$ACCOUNT_FLAG"
  if [ ! -f "$ACCOUNT_DIR/account.json" ]; then
    echo "[import] ERROR: Account $ACCOUNT_FLAG not found at $ACCOUNT_DIR"
    exit 1
  fi
  ACCOUNT_ID="$ACCOUNT_FLAG"
else
  # Auto-discover — must be exactly one account
  ACCOUNT_FILES=$(find "$ACCOUNTS_DIR" -maxdepth 2 -name "account.json" 2>/dev/null)
  ACCOUNT_FILE_COUNT=$(echo "$ACCOUNT_FILES" | grep -c '.' || true)

  if [ "$ACCOUNT_FILE_COUNT" -eq 0 ]; then
    echo "[import] ERROR: No accounts found in $ACCOUNTS_DIR"
    echo "[import] ERROR: Run the Maxy installer first."
    exit 1
  elif [ "$ACCOUNT_FILE_COUNT" -gt 1 ]; then
    echo "[import] ERROR: Multiple accounts found in $ACCOUNTS_DIR:"
    echo "$ACCOUNT_FILES" | while read -r f; do
      echo "[import]   $(dirname "$f" | xargs basename)"
    done
    echo "[import] ERROR: Specify which account with --account <uuid>"
    exit 1
  fi

  ACCOUNT_DIR=$(dirname "$(echo "$ACCOUNT_FILES" | head -1)")
  ACCOUNT_ID=$(basename "$ACCOUNT_DIR")
fi

echo "[import] Target account: $ACCOUNT_ID"
echo "[import] Account dir:    $ACCOUNT_DIR"

# ------------------------------------------------------------------
# Neo4j connection
# ------------------------------------------------------------------
NEO4J_URI="${NEO4J_URI:-bolt://localhost:7687}"
NEO4J_USER="${NEO4J_USER:-neo4j}"

NEO4J_PASSWORD_FILE="$INSTALL_DIR/platform/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 "[import] ERROR: Neo4j password not found."
  echo "[import]   Expected at: $NEO4J_PASSWORD_FILE"
  echo "[import]   Or set NEO4J_PASSWORD environment variable."
  exit 1
fi

CYPHER_SHELL="cypher-shell"
if ! command -v "$CYPHER_SHELL" &> /dev/null; then
  echo "[import] ERROR: cypher-shell not found. Install Neo4j or add cypher-shell to PATH."
  exit 1
fi

# Test connection
if ! "$CYPHER_SHELL" -u "$NEO4J_USER" -p "$NEO4J_PASSWORD" -a "$NEO4J_URI" \
    "RETURN 1" > /dev/null 2>&1; then
  echo "[import] ERROR: Cannot connect to Neo4j at $NEO4J_URI"
  echo "[import] ERROR: Is Neo4j running?"
  exit 1
fi
echo "[import] Neo4j connection OK ($NEO4J_URI)"

# ------------------------------------------------------------------
# Helper: escape a string for cypher-shell --param single-quoted value
# Cypher uses backslash escaping: ' → \' and \ → \\
# Uses python3 chr() codes to avoid bash quoting issues.
# ------------------------------------------------------------------
cypher_escape() {
  python3 -c "import sys; s=sys.argv[1]; s=s.replace(chr(92),chr(92)*2).replace(chr(39),chr(92)+chr(39)); print(s)" "$1"
}

# ------------------------------------------------------------------
# 1. Import contacts
# ------------------------------------------------------------------
CONTACTS_DIR="$BUNDLE_DIR/contacts"
CREATED=0
SKIPPED=0
ERRORS=0

if [ -d "$CONTACTS_DIR" ]; then
  for CONTACT_FILE in "$CONTACTS_DIR"/*.json; do
    [ -f "$CONTACT_FILE" ] || continue

    # Extract fields from contact JSON
    GIVEN_NAME=$(python3 -c "import json; d=json.load(open('$CONTACT_FILE')); print(d.get('givenName',''))" 2>/dev/null)
    FAMILY_NAME=$(python3 -c "import json; d=json.load(open('$CONTACT_FILE')); print(d.get('familyName',''))" 2>/dev/null)
    TELEPHONE=$(python3 -c "import json; d=json.load(open('$CONTACT_FILE')); print(d.get('telephone',''))" 2>/dev/null)
    EMAIL=$(python3 -c "import json; d=json.load(open('$CONTACT_FILE')); print(d.get('email',''))" 2>/dev/null)
    JOB_TITLE=$(python3 -c "import json; d=json.load(open('$CONTACT_FILE')); print(d.get('jobTitle',''))" 2>/dev/null)

    # Validate: must have givenName and at least phone or email
    if [ -z "$GIVEN_NAME" ]; then
      echo "[import] ERROR: contact skipped (no givenName): $(basename "$CONTACT_FILE")"
      ERRORS=$((ERRORS + 1))
      continue
    fi
    if [ -z "$TELEPHONE" ] && [ -z "$EMAIL" ]; then
      echo "[import] ERROR: contact skipped (no phone or email): $GIVEN_NAME"
      ERRORS=$((ERRORS + 1))
      continue
    fi

    # Build MERGE query with --param for safe parameterisation
    # MERGE on telephone (primary identifier for Taskmaster contacts)
    ESCAPED_GIVEN=$(cypher_escape "$GIVEN_NAME")
    ESCAPED_FAMILY=$(cypher_escape "$FAMILY_NAME")
    ESCAPED_TITLE=$(cypher_escape "$JOB_TITLE")

    # Build the cypher query — MERGE on telephone for dedup
    CYPHER_QUERY="MERGE (p:Person {telephone: \$phone, accountId: \$acct})"
    CYPHER_QUERY="$CYPHER_QUERY ON CREATE SET p.givenName = \$given, p.source = 'taskmaster-migration', p.status = 'customer', p.createdOn = datetime()"

    # Add optional fields on CREATE
    if [ -n "$FAMILY_NAME" ]; then
      CYPHER_QUERY="$CYPHER_QUERY, p.familyName = \$family"
    fi
    if [ -n "$EMAIL" ]; then
      CYPHER_QUERY="$CYPHER_QUERY, p.email = \$email"
    fi
    if [ -n "$JOB_TITLE" ]; then
      CYPHER_QUERY="$CYPHER_QUERY, p.jobTitle = \$title"
    fi

    CYPHER_QUERY="$CYPHER_QUERY RETURN p.givenName AS name, p.telephone AS phone, CASE WHEN p.createdOn = datetime() THEN 'created' ELSE 'exists' END AS status"

    # Execute with parameterised values
    PARAMS=(
      --param "phone => '$(cypher_escape "$TELEPHONE")'"
      --param "acct => '$(cypher_escape "$ACCOUNT_ID")'"
      --param "given => '$(cypher_escape "$GIVEN_NAME")'"
    )
    [ -n "$FAMILY_NAME" ] && PARAMS+=(--param "family => '$(cypher_escape "$FAMILY_NAME")'")
    [ -n "$EMAIL" ] && PARAMS+=(--param "email => '$(cypher_escape "$(echo "$EMAIL" | tr '[:upper:]' '[:lower:]')")'")
    [ -n "$JOB_TITLE" ] && PARAMS+=(--param "title => '$(cypher_escape "$JOB_TITLE")'")

    RESULT=$("$CYPHER_SHELL" -u "$NEO4J_USER" -p "$NEO4J_PASSWORD" -a "$NEO4J_URI" \
      "${PARAMS[@]}" "$CYPHER_QUERY" 2>&1) || {
      echo "[import] ERROR: contact failed: $GIVEN_NAME <$TELEPHONE> — $RESULT"
      ERRORS=$((ERRORS + 1))
      continue
    }

    if echo "$RESULT" | grep -q "created"; then
      echo "[import] contact created: $GIVEN_NAME <$TELEPHONE>"
      CREATED=$((CREATED + 1))
    else
      echo "[import] contact skipped (exists): $GIVEN_NAME <$TELEPHONE>"
      SKIPPED=$((SKIPPED + 1))
    fi
  done
fi
echo "[import] contacts: $CREATED created, $SKIPPED skipped, $ERRORS errors"

# ------------------------------------------------------------------
# 2. Import memory files
# ------------------------------------------------------------------
MEMORY_DIR="$BUNDLE_DIR/memory"
MEM_COUNT=0

if [ -d "$MEMORY_DIR" ]; then
  # Create target memory directories
  mkdir -p "$ACCOUNT_DIR/memory"

  for SUBDIR in shared admin public; do
    if [ -d "$MEMORY_DIR/$SUBDIR" ] && [ "$(ls -A "$MEMORY_DIR/$SUBDIR" 2>/dev/null)" ]; then
      # Copy recursively, preserving structure
      mkdir -p "$ACCOUNT_DIR/memory/$SUBDIR"
      cp -r "$MEMORY_DIR/$SUBDIR"/* "$ACCOUNT_DIR/memory/$SUBDIR/" 2>/dev/null || true
      COUNT=$(find "$ACCOUNT_DIR/memory/$SUBDIR" -type f 2>/dev/null | wc -l | tr -d ' ')
      echo "[import] memory/$SUBDIR: $COUNT files"
      MEM_COUNT=$((MEM_COUNT + COUNT))
    fi
  done
fi
echo "[import] memory total: $MEM_COUNT files"

# ------------------------------------------------------------------
# 3. Import conversations
# ------------------------------------------------------------------
CONVOS_DIR="$BUNDLE_DIR/conversations"
CONVO_COUNT=0

if [ -d "$CONVOS_DIR" ]; then
  mkdir -p "$ACCOUNT_DIR/conversations"

  # Admin conversations
  if [ -d "$CONVOS_DIR/admin" ] && [ "$(ls -A "$CONVOS_DIR/admin" 2>/dev/null)" ]; then
    mkdir -p "$ACCOUNT_DIR/conversations/admin"
    cp "$CONVOS_DIR/admin"/* "$ACCOUNT_DIR/conversations/admin/" 2>/dev/null || true
    COUNT=$(find "$ACCOUNT_DIR/conversations/admin" -type f 2>/dev/null | wc -l | tr -d ' ')
    echo "[import] conversations/admin: $COUNT files"
    CONVO_COUNT=$((CONVO_COUNT + COUNT))
  fi

  # Per-user conversations
  if [ -d "$CONVOS_DIR/users" ]; then
    for USER_CONVO in "$CONVOS_DIR/users"/*/; do
      [ -d "$USER_CONVO" ] || continue
      USER_KEY=$(basename "$USER_CONVO")
      mkdir -p "$ACCOUNT_DIR/conversations/users/$USER_KEY"
      cp "$USER_CONVO"* "$ACCOUNT_DIR/conversations/users/$USER_KEY/" 2>/dev/null || true
      COUNT=$(find "$ACCOUNT_DIR/conversations/users/$USER_KEY" -type f 2>/dev/null | wc -l | tr -d ' ')
      CONVO_COUNT=$((CONVO_COUNT + COUNT))
    done
    USER_DIRS=$(find "$CONVOS_DIR/users" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l | tr -d ' ')
    echo "[import] conversations/users: $USER_DIRS directories"
  fi

  # Group conversations
  if [ -d "$CONVOS_DIR/groups" ]; then
    for GRP_CONVO in "$CONVOS_DIR/groups"/*/; do
      [ -d "$GRP_CONVO" ] || continue
      GRP_ID=$(basename "$GRP_CONVO")
      mkdir -p "$ACCOUNT_DIR/conversations/groups/$GRP_ID"
      cp -r "$GRP_CONVO"* "$ACCOUNT_DIR/conversations/groups/$GRP_ID/" 2>/dev/null || true
      COUNT=$(find "$ACCOUNT_DIR/conversations/groups/$GRP_ID" -type f 2>/dev/null | wc -l | tr -d ' ')
      CONVO_COUNT=$((CONVO_COUNT + COUNT))
    done
    GRP_DIRS=$(find "$CONVOS_DIR/groups" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l | tr -d ' ')
    echo "[import] conversations/groups: $GRP_DIRS directories"
  fi
fi
echo "[import] conversations total: $CONVO_COUNT files"

# ------------------------------------------------------------------
# 4. Import media files
# ------------------------------------------------------------------
MEDIA_DIR="$BUNDLE_DIR/media"
MEDIA_COUNT=0

if [ -d "$MEDIA_DIR" ]; then
  mkdir -p "$ACCOUNT_DIR/media"

  for SUBDIR in admin public; do
    if [ -d "$MEDIA_DIR/$SUBDIR" ] && [ "$(ls -A "$MEDIA_DIR/$SUBDIR" 2>/dev/null)" ]; then
      mkdir -p "$ACCOUNT_DIR/media/$SUBDIR"
      cp "$MEDIA_DIR/$SUBDIR"/* "$ACCOUNT_DIR/media/$SUBDIR/" 2>/dev/null || true
      COUNT=$(find "$ACCOUNT_DIR/media/$SUBDIR" -type f 2>/dev/null | wc -l | tr -d ' ')
      echo "[import] media/$SUBDIR: $COUNT files"
      MEDIA_COUNT=$((MEDIA_COUNT + COUNT))
    fi
  done
fi
echo "[import] media total: $MEDIA_COUNT files"

# ------------------------------------------------------------------
# 5. Import automations (workflow definitions — reference only)
# ------------------------------------------------------------------
AUTO_DIR="$BUNDLE_DIR/automations"
AUTO_COUNT=0

if [ -d "$AUTO_DIR" ] && [ "$(ls -A "$AUTO_DIR" 2>/dev/null)" ]; then
  mkdir -p "$ACCOUNT_DIR/memory/admin/workflows-migrated"
  cp "$AUTO_DIR"/* "$ACCOUNT_DIR/memory/admin/workflows-migrated/" 2>/dev/null || true
  AUTO_COUNT=$(find "$ACCOUNT_DIR/memory/admin/workflows-migrated" -type f 2>/dev/null | wc -l | tr -d ' ')
  echo "[import] automations: $AUTO_COUNT workflow definitions → memory/admin/workflows-migrated/"
fi

# ------------------------------------------------------------------
# 6. Apply PINs (if present in bundle)
# ------------------------------------------------------------------
# The admin PIN lives in the brand's config directory (~/.realagent/.admin-pin
# or ~/.maxy/.admin-pin), not in account.json. Derive the config dir from
# the brand.json stamped into the install by the bundler.
BRAND_JSON="$INSTALL_DIR/platform/config/brand.json"
CONFIG_DIR_NAME=""
if [ -f "$BRAND_JSON" ]; then
  CONFIG_DIR_NAME=$(python3 -c "import json; print(json.load(open('$BRAND_JSON')).get('configDir',''))" 2>/dev/null)
fi

PINS_FILE="$BUNDLE_DIR/identity/pins.json"
if [ -f "$PINS_FILE" ]; then
  MASTER_PIN=$(python3 -c "import json; d=json.load(open('$PINS_FILE')); print(d.get('masterPin','') or '')" 2>/dev/null)
  if [ -n "$MASTER_PIN" ]; then
    if [ -n "$CONFIG_DIR_NAME" ]; then
      PIN_DIR="$HOME/$CONFIG_DIR_NAME"
      mkdir -p "$PIN_DIR"
      # The platform stores PINs as SHA-256 hashes, not plain text
      python3 -c "import hashlib; print(hashlib.sha256('$MASTER_PIN'.encode()).hexdigest())" > "$PIN_DIR/.admin-pin"
      echo "[import] masterPin hash written to $PIN_DIR/.admin-pin"
    else
      echo "[import] WARN: brand.json not found at $BRAND_JSON — cannot determine config directory for PIN"
      echo "[import] WARN: write the PIN manually: echo -n '$MASTER_PIN' > ~/.<brand>/.admin-pin"
    fi
  else
    echo "[import] No masterPin in bundle — skipping PIN application"
  fi
else
  echo "[import] No pins.json in bundle — skipping PIN application"
fi

# ------------------------------------------------------------------
# Summary
# ------------------------------------------------------------------
echo ""
echo "[import] =================================================="
echo "[import] Import complete: $ACCOUNT_ID"
echo "[import]   Contacts:      $CREATED created, $SKIPPED skipped, $ERRORS errors"
echo "[import]   Memory files:  $MEM_COUNT"
echo "[import]   Conversations: $CONVO_COUNT files"
echo "[import]   Media files:   $MEDIA_COUNT"
echo "[import]   Automations:   $AUTO_COUNT"
echo "[import] =================================================="

if [ "$ERRORS" -gt 0 ]; then
  echo "[import] WARNING: $ERRORS errors occurred during import — review log above"
  exit 1
fi
