/**
 * Improved Neural API - Clean, Consistent, Performant
 * 
 * Public API Surface:
 * - brain.neural.similar(a, b, options?)           // Similarity calculation  
 * - brain.neural.clusters(items?, options?)        // Semantic clustering
 * - brain.neural.neighbors(id, options?)           // K-nearest neighbors
 * - brain.neural.hierarchy(id, options?)           // Semantic hierarchy
 * - brain.neural.outliers(options?)                // Anomaly detection
 * - brain.neural.visualize(options?)               // Visualization data
 * 
 * Advanced Clustering:
 * - brain.neural.clusterByDomain(field, options?)  // Domain-aware clustering
 * - brain.neural.clusterByTime(field, windows, options?) // Temporal clustering
 * - brain.neural.clusterStream(options?)           // AsyncIterator for streaming
 * - brain.neural.updateClusters(items, options?)   // Incremental clustering
 * 
 * Private methods are prefixed with _ and not exposed in public API
 */

import { Vector } from '../coreTypes.js'
import { cosineDistance, euclideanDistance } from '../utils/distance.js'
import {
  SemanticCluster,
  DomainCluster,
  TemporalCluster,
  ExplainableCluster,
  ConfidentCluster,
  EnhancedSemanticCluster,
  ClusterEdge,
  SimilarityOptions,
  SimilarityResult,
  NeighborOptions,
  Neighbor,
  NeighborsResult,
  SemanticHierarchy,
  HierarchyOptions,
  ClusteringOptions,
  DomainClusteringOptions,
  TemporalClusteringOptions,
  StreamClusteringOptions,
  VisualizationOptions,
  VisualizationResult,
  OutlierOptions,
  Outlier,
  ClusteringResult,
  StreamingBatch,
  TimeWindow,
  ClusterFeedback,
  PerformanceMetrics,
  NeuralAPIConfig,
  NeuralAPIError,
  ClusteringError,
  SimilarityError
} from './types.js'

// ===== ADDITIONAL TYPE DEFINITIONS =====

/**
 * Graph structure for community detection
 */
interface GraphStructure {
  nodes: string[]
  edges: Map<string, Map<string, number>>
  nodeCount: number
  edgeCount: number
}

/**
 * Community detected from graph clustering
 */
interface Community {
  id: number
  members: string[]
  modularity: number
  density: number
  strongestConnections: Array<{from: string, to: string, weight: number}>
}

/**
 * Item with metadata for semantic clustering
 */
interface ItemWithMetadata {
  id: string
  vector: number[]
  metadata: Record<string, any>
  nounType: string
  label: string
  data?: any
}

export class ImprovedNeuralAPI {
  private brain: any // Brainy instance
  private config: NeuralAPIConfig
  
  // Caching for performance
  private similarityCache = new Map<string, number | SimilarityResult>()
  private clusterCache = new Map<string, ClusteringResult>()
  private hierarchyCache = new Map<string, SemanticHierarchy>()
  private neighborsCache = new Map<string, NeighborsResult>()
  
  // Performance tracking
  private performanceMetrics = new Map<string, PerformanceMetrics[]>()
  
  constructor(brain: any, config: NeuralAPIConfig = {}) {
    this.brain = brain
    this.config = {
      cacheSize: 1000,
      defaultAlgorithm: 'auto',
      similarityMetric: 'cosine',
      performanceTracking: true,
      maxMemoryUsage: '1GB',
      parallelProcessing: true,
      streamingBatchSize: 100,
      ...config
    }
    
    this._initializeCleanupTimer()
  }

  // ===== PUBLIC API: SIMILARITY =====

  /**
   * Calculate similarity between any two items (auto-detection)
   * Supports: IDs, text strings, vectors, or mixed types
   */
  async similar(
    a: string | Vector | any, 
    b: string | Vector | any, 
    options: SimilarityOptions = {}
  ): Promise<number | SimilarityResult> {
    const startTime = performance.now()
    
    try {
      // Create cache key
      const cacheKey = this._createSimilarityKey(a, b, options)
      if (this.similarityCache.has(cacheKey)) {
        return this.similarityCache.get(cacheKey)!
      }

      let result: number | SimilarityResult

      // Auto-detect input types and route accordingly
      if (this._isId(a) && this._isId(b)) {
        result = await this._similarityById(a, b, options)
      } else if (this._isVector(a) && this._isVector(b)) {
        result = await this._similarityByVector(a, b, options)
      } else if (typeof a === 'string' && typeof b === 'string') {
        result = await this._similarityByText(a, b, options)
      } else {
        // Mixed types - convert to vectors
        const vectorA = await this._convertToVector(a)
        const vectorB = await this._convertToVector(b)
        result = await this._similarityByVector(vectorA, vectorB, options)
      }

      // Cache result
      this._cacheResult(cacheKey, result, this.similarityCache)
      
      // Track performance
      this._trackPerformance('similarity', startTime, 2, 'mixed')
      
      return result
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error)
      throw new SimilarityError(`Failed to calculate similarity: ${errorMessage}`, {
        inputA: Array.isArray(a) ? 'vector' : typeof a === 'string' ? a.substring(0, 50) : 'unknown',
        inputB: Array.isArray(b) ? 'vector' : typeof b === 'string' ? b.substring(0, 50) : 'unknown',
        options
      })
    }
  }

  // ===== PUBLIC API: CLUSTERING =====

  /**
   * Intelligent semantic clustering with auto-routing
   * - No input: Cluster all data
   * - Array: Cluster specific items  
   * - String: Find clusters near this item
   * - Options object: Advanced configuration
   */
  async clusters(input?: string | string[] | ClusteringOptions): Promise<SemanticCluster[]> {
    const startTime = performance.now()
    
    try {
      let options: ClusteringOptions = {}
      let items: string[] | undefined

      // Parse input
      if (!input) {
        // Cluster all data
        items = undefined
        options = { algorithm: 'auto' }
      } else if (Array.isArray(input)) {
        // Cluster specific items
        items = input
        options = { algorithm: 'auto' }
      } else if (typeof input === 'string') {
        // Find clusters near this item
        const nearbyResult = await this.neighbors(input, { limit: 100 })
        items = nearbyResult.neighbors.map(n => n.id)
        options = { algorithm: 'auto' }
      } else if (typeof input === 'object') {
        // Configuration object
        options = input
        items = undefined
      } else {
        throw new ClusteringError('Invalid input for clustering', { input })
      }

      // Check cache
      const cacheKey = this._createClusteringKey(items, options)
      if (this.clusterCache.has(cacheKey)) {
        const cached = this.clusterCache.get(cacheKey)!
        return cached.clusters
      }

      // Route to optimal algorithm
      const result = await this._routeClusteringAlgorithm(items, options)
      
      // Cache result
      this._cacheResult(cacheKey, result, this.clusterCache)
      
      // Track performance
      this._trackPerformance('clustering', startTime, items?.length || 0, options.algorithm || 'auto')
      
      return result.clusters
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error)
      throw new ClusteringError(`Failed to perform clustering: ${errorMessage}`, {
        input: typeof input === 'object' ? JSON.stringify(input) : input,
      })
    }
  }

  /**
   * Fast hierarchical clustering using HNSW levels
   */
  async clusterFast(options: { level?: number; maxClusters?: number } = {}): Promise<SemanticCluster[]> {
    const fullOptions: ClusteringOptions = {
      algorithm: 'hierarchical',
      maxClusters: options.maxClusters,
      ...options
    }
    
    const result = await this._performHierarchicalClustering(undefined, fullOptions)
    return result.clusters
  }

  /**
   * Large-scale clustering with intelligent sampling
   */
  async clusterLarge(options: { sampleSize?: number; strategy?: 'random' | 'diverse' | 'recent' } = {}): Promise<SemanticCluster[]> {
    const fullOptions: ClusteringOptions = {
      algorithm: 'auto',
      sampleSize: options.sampleSize || 1000,
      strategy: options.strategy || 'diverse',
      ...options
    }
    
    const result = await this._performSampledClustering(undefined, fullOptions)
    return result.clusters
  }

  // ===== PUBLIC API: ADVANCED CLUSTERING =====

  /**
   * Domain-aware clustering based on metadata fields
   */
  async clusterByDomain(
    field: string, 
    options: DomainClusteringOptions = {}
  ): Promise<DomainCluster[]> {
    const startTime = performance.now()
    
    try {
      // Get all items with the specified field
      const items = await this._getItemsByField(field)
      if (items.length === 0) {
        return []
      }

      // Group by domain values
      const domainGroups = this._groupByDomain(items, field)
      const domainClusters: DomainCluster[] = []

      // Cluster within each domain
      for (const [domain, domainItems] of domainGroups) {
        const domainOptions: ClusteringOptions = {
          ...options,
          algorithm: 'auto',
          maxClusters: Math.min(options.maxClusters || 10, Math.ceil(domainItems.length / 3))
        }

        const clusters = await this._performClustering(domainItems.map(item => item.id), domainOptions)
        
        // Convert to domain clusters
        for (const cluster of clusters.clusters) {
          domainClusters.push({
            ...cluster,
            domain,
            domainConfidence: this._calculateDomainConfidence(cluster, domainItems),
            crossDomainMembers: options.crossDomainThreshold 
              ? await this._findCrossDomainMembers(cluster, options.crossDomainThreshold)
              : undefined
          })
        }
      }

      // Handle cross-domain clustering if enabled
      if (!options.preserveDomainBoundaries) {
        const crossDomainClusters = await this._findCrossDomainClusters(
          domainClusters, 
          options.crossDomainThreshold || 0.8
        )
        domainClusters.push(...crossDomainClusters)
      }

      this._trackPerformance('domainClustering', startTime, items.length, field)
      return domainClusters
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error)
      throw new ClusteringError(`Failed to cluster by domain: ${errorMessage}`, { field, options })
    }
  }

  /**
   * Temporal clustering based on time windows
   */
  async clusterByTime(
    timeField: string,
    windows: TimeWindow[],
    options: TemporalClusteringOptions = { timeField, windows }
  ): Promise<TemporalCluster[]> {
    const startTime = performance.now()
    
    try {
      const temporalClusters: TemporalCluster[] = []

      for (const window of windows) {
        // Get items in this time window
        const windowItems = await this._getItemsByTimeWindow(timeField, window)
        if (windowItems.length === 0) continue

        // Cluster items in this window  
        const clusteringOptions: ClusteringOptions = {
          ...options,
          algorithm: 'auto'
        }
        const clusters = await this._performClustering(
          windowItems.map(item => item.id),
          clusteringOptions
        )

        // Convert to temporal clusters
        for (const cluster of clusters.clusters) {
          const temporal = await this._calculateTemporalMetrics(cluster, windowItems, timeField)
          
          temporalClusters.push({
            ...cluster,
            timeWindow: window,
            trend: temporal.trend,
            temporal: temporal.metrics
          })
        }
      }

      // Handle overlapping windows
      if (options.overlapStrategy === 'merge') {
        return this._mergeOverlappingTemporalClusters(temporalClusters)
      }

      this._trackPerformance('temporalClustering', startTime, temporalClusters.length, 'temporal')
      return temporalClusters
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error)
      throw new ClusteringError(`Failed to cluster by time: ${errorMessage}`, { timeField, windows, options })
    }
  }

  /**
   * Streaming clustering with real-time updates
   */
  async *clusterStream(options: StreamClusteringOptions = {}): AsyncIterableIterator<StreamingBatch> {
    const batchSize = options.batchSize || this.config.streamingBatchSize || 100
    let batchNumber = 0
    let processedCount = 0
    
    try {
      // Get all items for processing
      const allItems = await this._getAllItemIds()
      const totalItems = allItems.length

      // Process in batches
      for (let i = 0; i < allItems.length; i += batchSize) {
        const startTime = performance.now()
        const batch = allItems.slice(i, i + batchSize)
        
        // Perform clustering on this batch
        const result = await this._performClustering(batch, {
          ...options,
          algorithm: 'auto',
          cacheResults: false // Don't cache streaming results
        })

        processedCount += batch.length
        const isComplete = processedCount >= totalItems

        yield {
          clusters: result.clusters,
          batchNumber: ++batchNumber,
          isComplete,
          progress: {
            processed: processedCount,
            total: totalItems,
            percentage: (processedCount / totalItems) * 100
          },
          metrics: {
            ...result.metrics,
            executionTime: performance.now() - startTime
          }
        }

        // Adaptive threshold adjustment
        if (options.adaptiveThreshold && batchNumber > 1) {
          options.threshold = this._adjustThresholdAdaptively(result.clusters, options.threshold)
        }
      }
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error)
      throw new ClusteringError(`Failed in streaming clustering: ${errorMessage}`, { options, batchNumber })
    }
  }

  /**
   * Incremental clustering - add new items to existing clusters
   */
  async updateClusters(newItems: string[], options: ClusteringOptions = {}): Promise<SemanticCluster[]> {
    const startTime = performance.now()
    
    try {
      // Get existing clusters
      const existingClusters = await this.clusters({ ...options, algorithm: 'auto' })
      
      // For each new item, find best cluster or create new one
      const updatedClusters = [...existingClusters]
      const unassignedItems: string[] = []

      for (const itemId of newItems) {
        let bestCluster: SemanticCluster | null = null
        let bestSimilarity = 0

        // Find most similar existing cluster
        for (const cluster of updatedClusters) {
          const similarity = await this._calculateItemToClusterSimilarity(itemId, cluster)
          if (similarity > bestSimilarity && similarity > (options.threshold || 0.6)) {
            bestSimilarity = similarity
            bestCluster = cluster
          }
        }

        if (bestCluster) {
          // Add to existing cluster
          bestCluster.members.push(itemId)
          bestCluster.size = bestCluster.members.length
          // Recalculate centroid
          bestCluster.centroid = await this._recalculateClusterCentroid(bestCluster)
        } else {
          // Item doesn't fit existing clusters
          unassignedItems.push(itemId)
        }
      }

      // Create new clusters for unassigned items
      if (unassignedItems.length > 0) {
        const newClusters = await this._performClustering(unassignedItems, options)
        updatedClusters.push(...newClusters.clusters)
      }

      this._trackPerformance('incrementalClustering', startTime, newItems.length, 'incremental')
      return updatedClusters
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error)
      throw new ClusteringError(`Failed to update clusters: ${errorMessage}`, { newItems, options })
    }
  }

  /**
   * Enhanced clustering with relationship analysis using verbs
   * Returns clusters with intra-cluster and inter-cluster relationship information
   * 
   * Scalable for millions of nodes - uses efficient batching and filtering
   */
  async clustersWithRelationships(
    input?: string | string[] | ClusteringOptions,
    options?: { batchSize?: number; maxRelationships?: number }
  ): Promise<EnhancedSemanticCluster[]> {
    const startTime = performance.now()
    const batchSize = options?.batchSize || 1000
    const maxRelationships = options?.maxRelationships || 10000
    let processedCount = 0
    
    try {
      // Get basic clusters first
      const basicClusters = await this.clusters(input)
      if (basicClusters.length === 0) {
        return []
      }
      
      // Build member lookup for O(1) cluster membership checking
      const memberToClusterMap = new Map<string, string>()
      const clusterMap = new Map<string, SemanticCluster>()
      
      for (const cluster of basicClusters) {
        clusterMap.set(cluster.id, cluster)
        for (const memberId of cluster.members) {
          memberToClusterMap.set(memberId, cluster.id)
        }
      }
      
      // Initialize cluster edge collections
      const clusterEdges = new Map<string, {
        intra: ClusterEdge[]
        inter: ClusterEdge[]
        edgeTypes: Record<string, number>
      }>()
      
      for (const cluster of basicClusters) {
        clusterEdges.set(cluster.id, {
          intra: [],
          inter: [],
          edgeTypes: {}
        })
      }
      
      // Process verbs in batches to handle millions of relationships efficiently
      let hasMoreVerbs = true
      let offset = 0
      
      while (hasMoreVerbs && processedCount < maxRelationships) {
        // Get batch of verbs using proper pagination API
        // Get all items and process in chunks (simplified approach)
        const allItems = await this.brain.find({ query: '', limit: Math.min(1000, maxRelationships) })
        const verbBatch = allItems.slice(offset, offset + batchSize)

        if (verbBatch.length === 0) {
          hasMoreVerbs = false
          break
        }
        
        // Process this batch
        for (const verb of verbBatch) {
          if (processedCount >= maxRelationships) break
          
          const sourceClusterId = memberToClusterMap.get(verb.sourceId)
          const targetClusterId = memberToClusterMap.get(verb.targetId)
          
          // Skip verbs that don't involve any clustered nodes
          if (!sourceClusterId && !targetClusterId) continue
          
          const edgeWeight = this._calculateEdgeWeight(verb)
          const edgeType = verb.verb || verb.type || 'relationship'
          
          if (sourceClusterId && targetClusterId) {
            if (sourceClusterId === targetClusterId) {
              // Intra-cluster relationship
              const edges = clusterEdges.get(sourceClusterId)!
              edges.intra.push({
                id: verb.id,
                source: verb.sourceId,
                target: verb.targetId,
                type: edgeType,
                weight: edgeWeight,
                isInterCluster: false,
                sourceCluster: sourceClusterId,
                targetCluster: sourceClusterId
              })
              edges.edgeTypes[edgeType] = (edges.edgeTypes[edgeType] || 0) + 1
            } else {
              // Inter-cluster relationship
              const sourceEdges = clusterEdges.get(sourceClusterId)!
              const targetEdges = clusterEdges.get(targetClusterId)!
              
              const edge: ClusterEdge = {
                id: verb.id,
                source: verb.sourceId,
                target: verb.targetId,
                type: edgeType,
                weight: edgeWeight,
                isInterCluster: true,
                sourceCluster: sourceClusterId,
                targetCluster: targetClusterId
              }
              
              sourceEdges.inter.push(edge)
              // Don't duplicate - target cluster will see this as incoming
              sourceEdges.edgeTypes[edgeType] = (sourceEdges.edgeTypes[edgeType] || 0) + 1
            }
          } else {
            // One-way relationship to/from cluster
            const clusterId = sourceClusterId || targetClusterId!
            const edges = clusterEdges.get(clusterId)!
            
            edges.inter.push({
              id: verb.id,
              source: verb.sourceId,
              target: verb.targetId,
              type: edgeType,
              weight: edgeWeight,
              isInterCluster: true,
              sourceCluster: sourceClusterId || 'external',
              targetCluster: targetClusterId || 'external'
            })
            edges.edgeTypes[edgeType] = (edges.edgeTypes[edgeType] || 0) + 1
          }
          
          processedCount++
        }
        
        offset += batchSize
        
        // Memory management: if we have too many edges, break early
        const totalEdges = Array.from(clusterEdges.values())
          .reduce((sum, edges) => sum + edges.intra.length + edges.inter.length, 0)
        
        if (totalEdges >= maxRelationships) {
          console.warn(`Relationship analysis stopped at ${totalEdges} edges to maintain performance`)
          break
        }
        
        // Check if we got fewer verbs than batch size (end of data)
        if (verbBatch.length < batchSize) {
          hasMoreVerbs = false
        }
      }
      
      // Build enhanced clusters
      const enhancedClusters: EnhancedSemanticCluster[] = []
      
      for (const cluster of basicClusters) {
        const edges = clusterEdges.get(cluster.id)!
        
        enhancedClusters.push({
          ...cluster,
          intraClusterEdges: edges.intra,
          interClusterEdges: edges.inter,
          relationshipSummary: {
            totalEdges: edges.intra.length + edges.inter.length,
            intraClusterEdges: edges.intra.length,
            interClusterEdges: edges.inter.length,
            edgeTypes: edges.edgeTypes
          }
        })
      }
      
      this._trackPerformance('clustersWithRelationships', startTime, processedCount, 'enhanced-scalable')
      return enhancedClusters
      
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error)
      throw new ClusteringError(`Failed to perform relationship-aware clustering: ${errorMessage}`, {
        input: typeof input === 'object' ? JSON.stringify(input) : input,
        processedCount: processedCount || 0
      })
    }
  }

  // ===== PUBLIC API: NEIGHBORS & HIERARCHY =====

  /**
   * Find K-nearest semantic neighbors
   */
  async neighbors(id: string, options: NeighborOptions = {}): Promise<NeighborsResult> {
    const startTime = performance.now()

    try {
      // Validate ID - throw for truly invalid, return empty for non-existent
      if (!id || id.length < 2) {
        throw new NeuralAPIError(
          'Invalid ID: ID must be a non-empty string with at least 2 characters',
          'INVALID_ID',
          { id, options }
        )
      }

      // For IDs that don't match hex pattern (non-existent but valid format), return empty gracefully
      if (!this._isValidEntityId(id)) {
        return {
          neighbors: [],
          queryId: id,
          totalFound: 0,
          averageSimilarity: 0
        }
      }

      const cacheKey = `neighbors:${id}:${JSON.stringify(options)}`
      if (this.neighborsCache.has(cacheKey)) {
        return this.neighborsCache.get(cacheKey)!
      }

      const limit = options.limit || 10
      const minSimilarity = options.minSimilarity || 0.1
      
      // Use HNSW index for efficient neighbor search
      const searchResults = await this.brain.find({
        query: '',
        limit: limit * 2, // Get more than needed for filtering
        where: options.includeMetadata ? {} : undefined
      })

      // Filter and sort neighbors
      const neighbors: Neighbor[] = []
      for (const result of searchResults) {
        if (result.id === id) continue // Skip self
        
        const similarity = await this._calculateSimilarity(id, result.id)
        if (similarity >= minSimilarity) {
          neighbors.push({
            id: result.id,
            similarity,
            data: result.content || result.data,
            metadata: options.includeMetadata ? result.metadata : undefined,
            distance: 1 - similarity
          })
        }

        if (neighbors.length >= limit) break
      }

      // Sort by specified criteria
      this._sortNeighbors(neighbors, options.sortBy || 'similarity')

      const result: NeighborsResult = {
        neighbors: neighbors.slice(0, limit),
        queryId: id,
        totalFound: neighbors.length,
        averageSimilarity: neighbors.reduce((sum, n) => sum + n.similarity, 0) / neighbors.length
      }

      this._cacheResult(cacheKey, result, this.neighborsCache)
      this._trackPerformance('neighbors', startTime, limit, 'knn')
      
      return result
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error)
      throw new NeuralAPIError(`Failed to find neighbors: ${errorMessage}`, 'NEIGHBORS_ERROR', { id, options })
    }
  }

  /**
   * Build semantic hierarchy around an item
   */
  async hierarchy(id: string, options: HierarchyOptions = {}): Promise<SemanticHierarchy> {
    const startTime = performance.now()

    const cacheKey = `hierarchy:${id}:${JSON.stringify(options)}`
    if (this.hierarchyCache.has(cacheKey)) {
      return this.hierarchyCache.get(cacheKey)!
    }

    // Get item data - handle non-existent and invalid IDs gracefully
    let item
    try {
      item = await this.brain.get(id)
    } catch (error) {
      // Handle validation errors, non-existent IDs, etc. gracefully
      // Return empty hierarchy instead of throwing
      return {
        root: null,
        levels: []
      }
    }

    if (!item) {
      // Return empty hierarchy for non-existent IDs
      return {
        root: null,
        levels: []
      }
    }

    // Build hierarchy based on strategy
    const hierarchy = await this._buildSemanticHierarchy(item, options)

    this._cacheResult(cacheKey, hierarchy, this.hierarchyCache)
    this._trackPerformance('hierarchy', startTime, 1, 'hierarchy')

    return hierarchy
  }

  // ===== PUBLIC API: ANALYSIS =====

  /**
   * Detect outliers and anomalous items
   */
  async outliers(options: OutlierOptions = {}): Promise<Outlier[]> {
    const startTime = performance.now()
    
    try {
      const threshold = options.threshold || 0.3
      const method = options.method || 'cluster-based'
      
      let outliers: Outlier[] = []

      switch (method) {
        case 'isolation':
          outliers = await this._detectOutliersIsolation(threshold, options)
          break
        case 'statistical':
          outliers = await this._detectOutliersStatistical(threshold, options)
          break
        case 'cluster-based':
        default:
          outliers = await this._detectOutliersClusterBased(threshold, options)
          break
      }

      this._trackPerformance('outlierDetection', startTime, outliers.length, method)
      return outliers
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error)
      throw new NeuralAPIError(`Failed to detect outliers: ${errorMessage}`, 'OUTLIER_ERROR', { options })
    }
  }

  /**
   * Generate visualization data for graph libraries
   */
  async visualize(options: VisualizationOptions = {}): Promise<VisualizationResult> {
    const startTime = performance.now()
    
    try {
      const maxNodes = options.maxNodes || 100
      const dimensions = options.dimensions || 2
      const algorithm = options.algorithm || 'force'
      
      // Get data for visualization
      const nodes = await this._generateVisualizationNodes(maxNodes, options)
      const edges = options.includeEdges ? await this._generateVisualizationEdges(nodes, options) : []
      const clusters = options.clusterColors ? await this._generateVisualizationClusters(nodes) : []
      
      // Apply layout algorithm
      const positionedNodes = await this._applyLayoutAlgorithm(nodes, edges, algorithm, dimensions)
      
      const result: VisualizationResult = {
        nodes: positionedNodes,
        edges,
        clusters,
        metadata: {
          algorithm,
          dimensions,
          totalNodes: nodes.length,
          totalEdges: edges.length,
          generatedAt: new Date()
        }
      }

      this._trackPerformance('visualization', startTime, nodes.length, algorithm)
      return result
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error)
      throw new NeuralAPIError(`Failed to generate visualization: ${errorMessage}`, 'VISUALIZATION_ERROR', { options })
    }
  }

  // ===== PRIVATE IMPLEMENTATION METHODS =====

  private async _routeClusteringAlgorithm(
    items: string[] | undefined, 
    options: ClusteringOptions
  ): Promise<ClusteringResult> {
    const algorithm = options.algorithm || 'auto'
    const itemCount = items?.length || await this._getTotalItemCount()

    // Auto-select optimal algorithm based on data size and characteristics
    if (algorithm === 'auto') {
      // Intelligent algorithm selection based on data characteristics
      const itemIds = items || await this._getAllItemIds()
      const dataCharacteristics = await this._analyzeDataCharacteristics(itemIds)
      
      const hasRichGraph = dataCharacteristics.graphDensity > 0.05
      const hasSemanticTypes = Object.keys(dataCharacteristics.typeDistribution).length > 3
      
      if (hasRichGraph && hasSemanticTypes) {
        // Best of all worlds for rich semantic graphs
        return this._performMultiModalClustering(items, { ...options, algorithm: 'multimodal' })
      } else if (hasRichGraph) {
        // Strong relationship network - use graph clustering
        return this._performGraphClustering(items, { ...options, algorithm: 'graph' })
      } else if (hasSemanticTypes) {
        // Rich semantic taxonomy - use semantic clustering
        return this._performSemanticClustering(items, { ...options, algorithm: 'semantic' })
      } else if (itemCount > 10000) {
        // Large dataset - use sampling
        return this._performSampledClustering(items, { ...options, algorithm: 'sample' })
      } else if (itemCount > 1000) {
        // Medium dataset - use hierarchical HNSW
        return this._performHierarchicalClustering(items, { ...options, algorithm: 'hierarchical' })
      } else {
        // Small dataset - use k-means for quality
        return this._performKMeansClustering(items, { ...options, algorithm: 'kmeans' })
      }
    }

    // Use specified algorithm
    switch (algorithm) {
      case 'hierarchical':
        return this._performHierarchicalClustering(items, options)
      case 'semantic':
        return this._performSemanticClustering(items, options)
      case 'graph':
        return this._performGraphClustering(items, options)
      case 'multimodal':
        return this._performMultiModalClustering(items, options)
      case 'kmeans':
        return this._performKMeansClustering(items, options)
      case 'dbscan':
        return this._performDBSCANClustering(items, options)
      case 'sample':
        return this._performSampledClustering(items, options)
      default:
        throw new ClusteringError(`Unsupported algorithm: ${algorithm}`)
    }
  }

  private async _performClustering(items: string[], options: ClusteringOptions): Promise<ClusteringResult> {
    // This is the main clustering dispatcher - routes to specific algorithms
    return this._routeClusteringAlgorithm(items, options)
  }

  // ===== REAL CLUSTERING IMPLEMENTATIONS =====

  /**
   * SEMANTIC-AWARE CLUSTERING: Uses existing NounType/VerbType taxonomy + HNSW
   */
  private async _performSemanticClustering(items: string[] | undefined, options: ClusteringOptions): Promise<ClusteringResult> {
    const startTime = performance.now()
    
    // Get all items if not specified
    const itemIds = items || await this._getAllItemIds()
    if (itemIds.length === 0) {
      return this._createEmptyResult(startTime, 'semantic')
    }

    // 1. Group items by semantic type (NounType) - O(n) operation
    const itemsWithMetadata = await this._getItemsWithMetadata(itemIds)
    const typeGroups = this._groupBySemanticType(itemsWithMetadata)
    
    const allClusters: SemanticCluster[] = []
    
    // 2. Cluster within each semantic type using HNSW - parallel processing
    const typeClusteringPromises = Array.from(typeGroups.entries()).map(async ([nounType, groupItems]) => {
      if (groupItems.length < (options.minClusterSize || 2)) {
        // Create single cluster for small groups
        return [{
          id: `semantic-${nounType}`,
          centroid: await this._calculateGroupCentroid(groupItems),
          members: groupItems.map(item => item.id),
          size: groupItems.length,
          confidence: 0.9, // High confidence for type-based clustering
          label: `${nounType} cluster`,
          metadata: { semanticType: nounType, clustering: 'semantic' }
        } as SemanticCluster]
      }

      // Use HNSW hierarchical clustering within type
      return this._clusterWithinSemanticType(groupItems, options)
    })

    const typeClusterResults = await Promise.all(typeClusteringPromises)
    typeClusterResults.forEach(clusters => allClusters.push(...clusters))

    // 3. Find cross-type relationships using existing verb connections
    const crossTypeConnections = await this._findCrossTypeConnections(typeGroups, options)
    
    // 4. Merge clusters that have strong cross-type relationships
    const finalClusters = await this._mergeSemanticClusters(allClusters, crossTypeConnections)

    return {
      clusters: finalClusters.slice(0, options.maxClusters || finalClusters.length),
      metrics: this._createPerformanceMetrics(startTime, itemIds.length, 'semantic'),
      metadata: {
        totalItems: itemIds.length,
        clustersFound: finalClusters.length,
        averageClusterSize: finalClusters.reduce((sum, c) => sum + c.size, 0) / finalClusters.length || 0,
        semanticTypes: Array.from(typeGroups.keys()).length,
        timestamp: new Date()
      }
    }
  }

  /**
   * HIERARCHICAL CLUSTERING: Uses existing HNSW levels for O(n) clustering
   */
  private async _performHierarchicalClustering(items: string[] | undefined, options: ClusteringOptions): Promise<ClusteringResult> {
    const startTime = performance.now()
    
    const itemIds = items || await this._getAllItemIds()
    if (itemIds.length === 0) {
      return this._createEmptyResult(startTime, 'hierarchical')
    }

    // Use existing HNSW level structure for natural clustering
    const level = (options as any).level || this._getOptimalClusteringLevel(itemIds.length)
    const maxClusters = options.maxClusters || Math.min(50, Math.ceil(itemIds.length / 20))
    
    // Get HNSW level representatives - these are natural cluster centers
    const levelNodes = await this._getHNSWLevelNodes(level)
    const clusterCenters = levelNodes.slice(0, maxClusters)
    
    const clusters: SemanticCluster[] = []
    
    // Create clusters around each level representative
    for (let i = 0; i < clusterCenters.length; i++) {
      const center = clusterCenters[i]
      
      // Find items that belong to this cluster using HNSW neighbors
      const members = await this._findClusterMembers(center, itemIds, 0.5)
      
      if (members.length > 0) {
        // Get actual node data for creating cluster
        const memberData = await this._getItemsWithMetadata(members)
        const centroid = await this._calculateCentroidFromItems(memberData)
        
        clusters.push({
          id: `hierarchical-${i}`,
          centroid,
          members,
          size: members.length,
          confidence: await this._calculateHierarchicalConfidence(members),
          label: await this._generateClusterLabel(memberData, 'hierarchical'),
          metadata: { level, clusterCenter: center, clustering: 'hierarchical' }
        })
      }
    }

    // Assign remaining items to nearest clusters
    const assignedItems = new Set(clusters.flatMap(c => c.members))
    const unassignedItems = itemIds.filter(id => !assignedItems.has(id))
    
    if (unassignedItems.length > 0) {
      await this._assignUnassignedItems(unassignedItems, clusters)
    }

    return {
      clusters,
      metrics: this._createPerformanceMetrics(startTime, itemIds.length, 'hierarchical'),
      metadata: {
        totalItems: itemIds.length,
        clustersFound: clusters.length,
        averageClusterSize: clusters.reduce((sum, c) => sum + c.size, 0) / clusters.length || 0,
        hnswLevel: level,
        timestamp: new Date()
      }
    }
  }

  /**
   * K-MEANS CLUSTERING: Real implementation using existing distance functions
   */
  private async _performKMeansClustering(items: string[] | undefined, options: ClusteringOptions): Promise<ClusteringResult> {
    const startTime = performance.now()
    
    const itemIds = items || await this._getAllItemIds()
    if (itemIds.length === 0) {
      return this._createEmptyResult(startTime, 'kmeans')
    }

    // Get vectors for all items using existing infrastructure
    const itemsWithVectors = await this._getItemsWithVectors(itemIds)
    
    // Determine optimal k
    const k = options.maxClusters || Math.min(
      Math.floor(Math.sqrt(itemsWithVectors.length / 2)),
      50 // Maximum clusters for practical use
    )
    
    if (k <= 1) {
      // Single cluster case
      return {
        clusters: [{
          id: 'kmeans-single',
          centroid: await this._calculateCentroidFromItems(itemsWithVectors),
          members: itemIds,
          size: itemIds.length,
          confidence: 1.0,
          label: 'Single cluster',
          metadata: { clustering: 'kmeans', k: 1 }
        }],
        metrics: this._createPerformanceMetrics(startTime, itemIds.length, 'kmeans'),
        metadata: {
          totalItems: itemIds.length,
          clustersFound: 1,
          averageClusterSize: itemIds.length,
          kValue: 1,
          timestamp: new Date()
        }
      }
    }

    // Initialize centroids using k-means++ for better convergence
    const centroids = await this._initializeCentroidsKMeansPlusPlus(itemsWithVectors, k)
    
    let assignments: number[] = new Array(itemsWithVectors.length).fill(0)
    let hasConverged = false
    const maxIterations = options.maxIterations || 100
    const tolerance = options.tolerance || 1e-4
    
    // K-means iteration loop
    for (let iteration = 0; iteration < maxIterations && !hasConverged; iteration++) {
      // Assignment step: assign each point to nearest centroid
      const newAssignments = await this._assignPointsToCentroids(itemsWithVectors, centroids)
      
      // Update step: recalculate centroids
      const newCentroids = await this._updateCentroids(itemsWithVectors, newAssignments, k)
      
      // Check convergence: has assignment changed significantly?
      const changeRate = this._calculateAssignmentChangeRate(assignments, newAssignments)
      hasConverged = changeRate < tolerance
      
      assignments = newAssignments
      
      // Update centroids for next iteration
      for (let i = 0; i < centroids.length; i++) {
        centroids[i] = newCentroids[i]
      }
    }

    // Create semantic clusters from k-means results
    const clusters: SemanticCluster[] = []
    for (let clusterIndex = 0; clusterIndex < k; clusterIndex++) {
      const clusterMembers = itemsWithVectors.filter((_, i) => assignments[i] === clusterIndex)
      
      if (clusterMembers.length > 0) {
        const memberIds = clusterMembers.map(item => item.id)
        
        clusters.push({
          id: `kmeans-${clusterIndex}`,
          centroid: centroids[clusterIndex],
          members: memberIds,
          size: memberIds.length,
          confidence: await this._calculateKMeansClusterConfidence(clusterMembers, centroids[clusterIndex]),
          label: await this._generateClusterLabel(clusterMembers, 'kmeans'),
          metadata: { 
            clustering: 'kmeans', 
            k, 
            clusterIndex,
            convergenceIterations: maxIterations
          }
        })
      }
    }

    return {
      clusters,
      metrics: this._createPerformanceMetrics(startTime, itemIds.length, 'kmeans'),
      metadata: {
        totalItems: itemIds.length,
        clustersFound: clusters.length,
        averageClusterSize: clusters.reduce((sum, c) => sum + c.size, 0) / clusters.length || 0,
        kValue: k,
        hasConverged,
        timestamp: new Date()
      }
    }
  }

  /**
   * DBSCAN CLUSTERING: Density-based clustering with adaptive parameters using HNSW
   */
  private async _performDBSCANClustering(items: string[] | undefined, options: ClusteringOptions): Promise<ClusteringResult> {
    const startTime = performance.now()
    
    const itemIds = items || await this._getAllItemIds()
    if (itemIds.length === 0) {
      return this._createEmptyResult(startTime, 'dbscan')
    }

    const itemsWithVectors = await this._getItemsWithVectors(itemIds)
    
    // Adaptive parameter selection using HNSW neighbors
    const minPts = options.minClusterSize || Math.max(4, Math.floor(Math.log2(itemsWithVectors.length)))
    const eps = options.threshold || await this._estimateOptimalEps(itemsWithVectors, minPts)
    
    // DBSCAN state tracking
    const NOISE = -1
    const UNVISITED = 0
    const visited = new Map<string, boolean>()
    const clusterAssignments = new Map<string, number>()
    let currentClusterId = 1
    
    // Process each point
    for (const item of itemsWithVectors) {
      if (visited.get(item.id)) continue
      
      visited.set(item.id, true)
      
      // Find neighbors using existing HNSW infrastructure for efficiency
      const neighbors = await this._findNeighborsWithinEps(item, itemsWithVectors, eps)
      
      if (neighbors.length < minPts) {
        // Mark as noise (outlier)
        clusterAssignments.set(item.id, NOISE)
      } else {
        // Start new cluster
        await this._expandCluster(
          item,
          neighbors,
          currentClusterId,
          eps,
          minPts,
          itemsWithVectors,
          visited,
          clusterAssignments
        )
        currentClusterId++
      }
    }

    // Convert DBSCAN results to SemanticCluster format
    const clusters: SemanticCluster[] = []
    const clusterGroups = new Map<number, string[]>()
    const outliers: string[] = []
    
    // Group items by cluster assignment
    for (const [itemId, clusterId] of clusterAssignments) {
      if (clusterId === NOISE) {
        outliers.push(itemId)
      } else {
        if (!clusterGroups.has(clusterId)) {
          clusterGroups.set(clusterId, [])
        }
        clusterGroups.get(clusterId)!.push(itemId)
      }
    }
    
    // Create SemanticCluster objects
    for (const [clusterId, memberIds] of clusterGroups) {
      if (memberIds.length > 0) {
        const members = itemsWithVectors.filter(item => memberIds.includes(item.id))
        
        clusters.push({
          id: `dbscan-${clusterId}`,
          centroid: await this._calculateCentroidFromItems(members),
          members: memberIds,
          size: memberIds.length,
          confidence: await this._calculateDBSCANClusterConfidence(members, eps),
          label: await this._generateClusterLabel(members, 'dbscan'),
          metadata: { 
            clustering: 'dbscan',
            clusterId,
            eps,
            minPts,
            isDensityBased: true
          }
        })
      }
    }

    // Handle outliers - optionally create outlier cluster or assign to nearest
    if (outliers.length > 0 && options.includeOutliers) {
      const outlierMembers = itemsWithVectors.filter(item => outliers.includes(item.id))
      
      clusters.push({
        id: 'dbscan-outliers',
        centroid: await this._calculateCentroidFromItems(outlierMembers),
        members: outliers,
        size: outliers.length,
        confidence: 0.1, // Low confidence for outliers
        label: 'Outliers',
        metadata: { 
          clustering: 'dbscan',
          isOutlierCluster: true,
          eps,
          minPts
        }
      })
    }

    return {
      clusters,
      metrics: this._createPerformanceMetrics(startTime, itemIds.length, 'dbscan'),
      metadata: {
        totalItems: itemIds.length,
        clustersFound: clusters.length,
        averageClusterSize: clusters.reduce((sum, c) => sum + c.size, 0) / clusters.length || 0,
        outlierCount: outliers.length,
        eps,
        minPts,
        timestamp: new Date()
      }
    }
  }

  /**
   * GRAPH COMMUNITY DETECTION: Uses existing verb relationships for clustering  
   */
  private async _performGraphClustering(items: string[] | undefined, options: ClusteringOptions): Promise<ClusteringResult> {
    const startTime = performance.now()
    
    const itemIds = items || await this._getAllItemIds()
    if (itemIds.length === 0) {
      return this._createEmptyResult(startTime, 'graph')
    }

    // Build graph from existing verb relationships
    const graph = await this._buildGraphFromVerbs(itemIds, options)
    
    // Detect communities using modularity optimization
    const communities = await this._detectCommunities(graph, options)
    
    // Enhance communities with vector similarity for boundary refinement
    const refinedCommunities = await this._refineCommunitiesWithVectors(communities, options)
    
    // Convert to SemanticCluster format with Triple Intelligence labeling
    const clusters: SemanticCluster[] = []
    
    for (let i = 0; i < refinedCommunities.length; i++) {
      const community = refinedCommunities[i]
      
      if (community.members.length > 0) {
        const members = await this._getItemsWithMetadata(community.members)
        
        // Use Triple Intelligence for intelligent cluster labeling
        const clusterLabel = await this._generateIntelligentClusterLabel(members, 'graph')
        const clusterCentroid = await this._calculateCentroidFromItems(members)
        
        clusters.push({
          id: `graph-${i}`,
          centroid: clusterCentroid,
          members: community.members,
          size: community.members.length,
          confidence: community.modularity || 0.7,
          label: clusterLabel,
          metadata: { 
            clustering: 'graph',
            communityId: i,
            modularity: community.modularity,
            graphDensity: community.density,
            strongestConnections: community.strongestConnections
          }
        })
      }
    }

    return {
      clusters,
      metrics: this._createPerformanceMetrics(startTime, itemIds.length, 'graph'),
      metadata: {
        totalItems: itemIds.length,
        clustersFound: clusters.length,
        averageClusterSize: clusters.reduce((sum, c) => sum + c.size, 0) / clusters.length || 0,
        averageModularity: clusters.reduce((sum, c) => sum + (c.metadata?.modularity || 0), 0) / clusters.length || 0,
        timestamp: new Date()
      }
    }
  }

  /**
   * MULTI-MODAL FUSION: Combines vector + graph + semantic + Triple Intelligence
   */
  private async _performMultiModalClustering(items: string[] | undefined, options: ClusteringOptions): Promise<ClusteringResult> {
    const startTime = performance.now()
    
    const itemIds = items || await this._getAllItemIds()
    if (itemIds.length === 0) {
      return this._createEmptyResult(startTime, 'multimodal')
    }

    // Run multiple clustering algorithms in parallel
    const [vectorClusters, graphClusters, semanticClusters] = await Promise.all([
      this._performHierarchicalClustering(itemIds, { ...options, algorithm: 'hierarchical' }),
      this._performGraphClustering(itemIds, { ...options, algorithm: 'graph' }),
      this._performSemanticClustering(itemIds, { ...options, algorithm: 'semantic' })
    ])

    // Fuse results using intelligent consensus with Triple Intelligence
    const fusedClusters = await this._fuseClusteringResultsWithTripleIntelligence(
      [vectorClusters.clusters, graphClusters.clusters, semanticClusters.clusters],
      options
    )

    return {
      clusters: fusedClusters,
      metrics: this._createPerformanceMetrics(startTime, itemIds.length, 'multimodal'),
      metadata: {
        totalItems: itemIds.length,
        clustersFound: fusedClusters.length,
        averageClusterSize: fusedClusters.reduce((sum, c) => sum + c.size, 0) / fusedClusters.length || 0,
        fusionMethod: 'triple_intelligence_consensus',
        componentAlgorithms: ['hierarchical', 'graph', 'semantic'],
        timestamp: new Date()
      }
    }
  }

  /**
   * SAMPLED CLUSTERING: For very large datasets using intelligent sampling
   */
  private async _performSampledClustering(items: string[] | undefined, options: ClusteringOptions): Promise<ClusteringResult> {
    const startTime = performance.now()
    
    const itemIds = items || await this._getAllItemIds()
    if (itemIds.length === 0) {
      return this._createEmptyResult(startTime, 'sampled')
    }

    const sampleSize = Math.min(options.sampleSize || 1000, itemIds.length)
    const strategy = options.strategy || 'diverse'
    
    // Intelligent sampling using existing infrastructure
    const sample = await this._getSampleUsingStrategy(itemIds, sampleSize, strategy)
    
    // Cluster the sample using the best algorithm for the sample size
    const sampleResult = await this._performHierarchicalClustering(sample, {
      ...options,
      maxClusters: Math.min(options.maxClusters || 50, Math.ceil(sample.length / 10))
    })
    
    // Project clusters back to full dataset using HNSW neighbors
    const projectedClusters = await this._projectClustersToFullDataset(
      sampleResult.clusters, 
      itemIds, 
      sample
    )

    return {
      clusters: projectedClusters,
      metrics: this._createPerformanceMetrics(startTime, itemIds.length, 'sampled'),
      metadata: {
        totalItems: itemIds.length,
        sampleSize: sample.length,
        samplingStrategy: strategy,
        clustersFound: projectedClusters.length,
        averageClusterSize: projectedClusters.reduce((sum, c) => sum + c.size, 0) / projectedClusters.length || 0,
        timestamp: new Date()
      }
    }
  }

  // Similarity implementation methods
  private async _similarityById(id1: string, id2: string, options: SimilarityOptions): Promise<number | SimilarityResult> {
    // Get vectors for both items
    const item1 = await this.brain.get(id1)
    const item2 = await this.brain.get(id2)
    
    if (!item1 || !item2) {
      return 0
    }

    return this._similarityByVector(item1.vector, item2.vector, options)
  }

  private async _similarityByVector(v1: Vector, v2: Vector, options: SimilarityOptions): Promise<number | SimilarityResult> {
    const metric = options.metric || this.config.similarityMetric || 'cosine'
    let score = 0

    switch (metric) {
      case 'cosine':
        score = 1 - cosineDistance(v1, v2)
        break
      case 'euclidean':
        score = 1 / (1 + euclideanDistance(v1, v2))
        break
      case 'manhattan':
        score = 1 / (1 + this._manhattanDistance(v1, v2))
        break
      default:
        score = 1 - cosineDistance(v1, v2)
    }

    if (options.detailed) {
      return {
        score: options.normalized !== false ? Math.max(0, Math.min(1, score)) : score,
        confidence: this._calculateConfidence(score, v1, v2),
        explanation: this._generateSimilarityExplanation(score, metric),
        metric
      }
    }

    return options.normalized !== false ? Math.max(0, Math.min(1, score)) : score
  }

  private async _similarityByText(text1: string, text2: string, options: SimilarityOptions): Promise<number | SimilarityResult> {
    // Convert text to vectors using brain's embedding function
    const vector1 = await this.brain.embed(text1)
    const vector2 = await this.brain.embed(text2)
    
    return this._similarityByVector(vector1, vector2, options)
  }

  // Utility methods for internal operations
  private _isId(value: any): boolean {
    return typeof value === 'string' &&
           ((value.length === 36 && value.includes('-')) || // UUID-like
            (value.length > 10 && !value.includes(' '))) // ID-like string
  }

  private _isVector(value: any): boolean {
    return Array.isArray(value) &&
           value.length > 0 &&
           typeof value[0] === 'number'
  }

  private _isValidEntityId(id: string): boolean {
    // Validate ID format - must start with 2 hex characters for sharding
    // This prevents errors in storage layer that uses first 2 chars as shard key
    if (typeof id !== 'string' || id.length < 2) return false
    return /^[0-9a-f]{2}/i.test(id.substring(0, 2))
  }

  private async _convertToVector(input: any): Promise<Vector> {
    if (this._isVector(input)) {
      return input
    } else if (this._isId(input)) {
      const item = await this.brain.get(input)
      return item?.vector || []
    } else if (typeof input === 'string') {
      return await this.brain.embed(input)
    } else {
      throw new Error(`Cannot convert input to vector: ${typeof input}`)
    }
  }

  private _createSimilarityKey(a: any, b: any, options: SimilarityOptions): string {
    const aKey = typeof a === 'object' ? JSON.stringify(a).substring(0, 50) : String(a)
    const bKey = typeof b === 'object' ? JSON.stringify(b).substring(0, 50) : String(b)
    return `${aKey}|${bKey}|${JSON.stringify(options)}`
  }

  private _createClusteringKey(items: string[] | undefined, options: ClusteringOptions): string {
    const itemsKey = items ? [...items].sort().join(',') : 'all'
    return `clustering:${itemsKey}:${JSON.stringify(options)}`
  }

  private _cacheResult<T>(key: string, result: T, cache: Map<string, T>): void {
    if (cache.size >= (this.config.cacheSize || 1000)) {
      // Remove oldest entries (simple LRU)
      const firstKey = cache.keys().next().value
      if (firstKey) cache.delete(firstKey)
    }
    cache.set(key, result)
  }

  private _trackPerformance(operation: string, startTime: number, itemCount: number, algorithm: string): void {
    if (!this.config.performanceTracking) return

    const metrics: PerformanceMetrics = {
      executionTime: performance.now() - startTime,
      memoryUsed: 0, // Would implement actual memory tracking
      itemsProcessed: itemCount,
      cacheHits: 0, // Would track actual cache hits
      cacheMisses: 0, // Would track actual cache misses
      algorithm
    }

    if (!this.performanceMetrics.has(operation)) {
      this.performanceMetrics.set(operation, [])
    }
    this.performanceMetrics.get(operation)!.push(metrics)
  }

  private _createPerformanceMetrics(startTime: number, itemCount: number, algorithm: string): PerformanceMetrics {
    return {
      executionTime: performance.now() - startTime,
      memoryUsed: 0,
      itemsProcessed: itemCount,
      cacheHits: 0,
      cacheMisses: 0,
      algorithm
    }
  }

  private _initializeCleanupTimer(): void {
    // Periodically clean up caches to prevent memory leaks
    setInterval(() => {
      if (this.similarityCache.size > (this.config.cacheSize || 1000)) {
        this.similarityCache.clear()
      }
      if (this.clusterCache.size > (this.config.cacheSize || 1000)) {
        this.clusterCache.clear()
      }
      if (this.hierarchyCache.size > (this.config.cacheSize || 1000)) {
        this.hierarchyCache.clear()
      }
      if (this.neighborsCache.size > (this.config.cacheSize || 1000)) {
        this.neighborsCache.clear()
      }
    }, 300000) // Clean every 5 minutes
  }

  // ===== GRAPH COMMUNITY DETECTION UTILITIES =====
  
  /**
   * Build graph structure from existing verb relationships
   */
  private async _buildGraphFromVerbs(itemIds: string[], options: ClusteringOptions): Promise<GraphStructure> {
    const nodes = new Set(itemIds)
    const edges = new Map<string, Map<string, number>>()
    const verbWeights = new Map<string, number>()
    
    // Initialize verb relationship weights
    const relationshipWeights = {
      'creates': 1.0,
      'partOf': 0.9,
      'contains': 0.9,
      'relatedTo': 0.7,
      'references': 0.6,
      'causes': 0.8,
      'dependsOn': 0.8,
      'memberOf': 0.9,
      'worksWith': 0.7,
      'communicates': 0.6
    }
    
    // Get all verbs connecting the items
    for (const sourceId of itemIds) {
      const sourceVerbs = await this.brain.getRelations(sourceId)
      
      for (const verb of sourceVerbs) {
        const targetId = verb.target
        
        if (nodes.has(targetId) && sourceId !== targetId) {
          // Initialize edge map if needed
          if (!edges.has(sourceId)) {
            edges.set(sourceId, new Map())
          }
          
          // Calculate edge weight from verb type and metadata
          const verbType = verb.verb
          const baseWeight = (relationshipWeights as Record<string, number>)[verbType] || 0.5
          const confidenceWeight = verb.confidence || 1.0
          const weight = baseWeight * confidenceWeight
          
          // Add or strengthen edge
          const currentWeight = edges.get(sourceId)?.get(targetId) || 0
          edges.get(sourceId)!.set(targetId, Math.min(currentWeight + weight, 1.0))
          
          // Make graph undirected by adding reverse edge
          if (!edges.has(targetId)) {
            edges.set(targetId, new Map())
          }
          const reverseWeight = edges.get(targetId)?.get(sourceId) || 0
          edges.get(targetId)!.set(sourceId, Math.min(reverseWeight + weight, 1.0))
        }
      }
    }
    
    return {
      nodes: Array.from(nodes),
      edges,
      nodeCount: nodes.size,
      edgeCount: Array.from(edges.values()).reduce((sum, edgeMap) => sum + edgeMap.size, 0) / 2 // Undirected
    }
  }
  
  /**
   * Detect communities using Louvain modularity optimization
   */
  private async _detectCommunities(graph: GraphStructure, options: ClusteringOptions): Promise<Community[]> {
    const { nodes, edges } = graph
    
    // Initialize each node as its own community
    const communities = new Map<string, number>()
    nodes.forEach((node, index) => communities.set(node, index))
    
    const totalWeight = this._calculateTotalWeight(edges)
    let improved = true
    let iteration = 0
    const maxIterations = 50
    
    // Louvain algorithm: iteratively move nodes to communities that maximize modularity
    while (improved && iteration < maxIterations) {
      improved = false
      iteration++
      
      for (const node of nodes) {
        const currentCommunity = communities.get(node)!
        let bestCommunity = currentCommunity
        let bestGain = 0
        
        // Consider neighboring communities
        const neighborCommunities = this._getNeighborCommunities(node, edges, communities)
        
        for (const neighborCommunity of neighborCommunities) {
          if (neighborCommunity !== currentCommunity) {
            const gain = this._calculateModularityGain(
              node, 
              currentCommunity, 
              neighborCommunity, 
              edges, 
              communities, 
              totalWeight
            )
            
            if (gain > bestGain) {
              bestGain = gain
              bestCommunity = neighborCommunity
            }
          }
        }
        
        // Move node if beneficial
        if (bestCommunity !== currentCommunity) {
          communities.set(node, bestCommunity)
          improved = true
        }
      }
    }
    
    // Group nodes by final community assignment
    const communityGroups = new Map<number, string[]>()
    for (const [node, communityId] of communities) {
      if (!communityGroups.has(communityId)) {
        communityGroups.set(communityId, [])
      }
      communityGroups.get(communityId)!.push(node)
    }
    
    // Convert to Community objects with metadata
    const result: Community[] = []
    for (const [communityId, members] of communityGroups) {
      if (members.length >= (options.minClusterSize || 2)) {
        const modularity = this._calculateCommunityModularity(members, edges, totalWeight)
        const density = this._calculateCommunityDensity(members, edges)
        const strongestConnections = this._findStrongestConnections(members, edges, 3)
        
        result.push({
          id: communityId,
          members,
          modularity,
          density,
          strongestConnections
        })
      }
    }
    
    return result
  }
  
  /**
   * Refine community boundaries using vector similarity
   */
  private async _refineCommunitiesWithVectors(
    communities: Community[], 
    options: ClusteringOptions
  ): Promise<Community[]> {
    const refined: Community[] = []
    
    for (const community of communities) {
      const membersWithVectors = await this._getItemsWithVectors(community.members)
      
      // Check if community is coherent in vector space
      const vectorCoherence = await this._calculateVectorCoherence(membersWithVectors)
      
      if (vectorCoherence > 0.3) {
        // Community is coherent, keep as is
        refined.push(community)
      } else {
        // Split community using vector-based sub-clustering
        const subClusters = await this._performHierarchicalClustering(
          community.members, 
          { ...options, maxClusters: Math.ceil(community.members.length / 5) }
        )
        
        // Convert sub-clusters to communities
        for (let i = 0; i < subClusters.clusters.length; i++) {
          const subCluster = subClusters.clusters[i]
          refined.push({
            id: community.id * 1000 + i, // Unique sub-community ID
            members: subCluster.members,
            modularity: community.modularity * 0.8, // Slightly lower modularity for sub-communities
            density: community.density,
            strongestConnections: []
          })
        }
      }
    }
    
    return refined
  }

  // ===== SEMANTIC CLUSTERING UTILITIES =====
  
  /**
   * Get items with their metadata including noun types
   */
  private async _getItemsWithMetadata(itemIds: string[]): Promise<ItemWithMetadata[]> {
    const items = await Promise.all(
      itemIds.map(async id => {
        const noun = await this.brain.get(id)
        if (!noun) {
          return null
        }
        return {
          id,
          vector: noun.vector || [],
          metadata: noun.metadata || {},
          nounType: noun.metadata?.noun || noun.metadata?.nounType || 'content',
          label: noun.metadata?.label || noun.metadata?.data || id,
          data: noun.metadata
        } as ItemWithMetadata
      })
    )
    
    return items.filter((item): item is ItemWithMetadata => 
      item !== null
    )
  }
  
  /**
   * Group items by their semantic noun types
   */
  private _groupBySemanticType(items: ItemWithMetadata[]): Map<string, ItemWithMetadata[]> {
    const groups = new Map<string, ItemWithMetadata[]>()
    
    for (const item of items) {
      const type = item.nounType
      if (!groups.has(type)) {
        groups.set(type, [])
      }
      groups.get(type)!.push(item)
    }
    
    return groups
  }

  // Iterator-based implementations for scalability
  /**
   * Iterate through all items without loading them all at once
   * This scales to millions of items without memory issues
   */
  private async *_iterateAllItems(options?: { batchSize?: number }): AsyncGenerator<any> {
    const batchSize = options?.batchSize || 1000
    let cursor: string | undefined
    let hasMore = true

    while (hasMore) {
      const result = await this.brain.find({
        query: '',
        limit: batchSize,
        cursor
      })

      for (const item of result.items || result) {
        yield item
      }

      hasMore = result.hasMore || false
      cursor = result.nextCursor

      // Safety check to prevent infinite loops
      if (!result.items || result.items.length === 0) {
        break
      }
    }
  }

  /**
   * Get a sample of item IDs for operations that don't need all items
   * This is O(1) for small samples
   */
  private async _getSampleItemIds(sampleSize: number = 1000): Promise<string[]> {
    const result = await this.brain.find({
      query: '',
      limit: Math.min(sampleSize, 10000)  // Cap at 10k for safety
    })

    const items = result.items || result
    return items.map((item: any) => item.entity?.id || item.id).filter((id: any) => id)
  }

  /**
   * Get total count using the brain's O(1) counting API
   */
  private async _getTotalItemCount(): Promise<number> {
    // Use the brain's O(1) counting API if available
    if (this.brain.counts && typeof this.brain.counts.entities === 'function') {
      return await this.brain.counts.entities()
    }

    // Fallback: Get from storage statistics
    const storage = (this.brain as any).storage
    if (storage && typeof storage.getStatistics === 'function') {
      const stats = await storage.getStatistics()
      return stats?.totalNodes || 0
    }

    // Last resort: Sample and estimate
    const sample = await this.brain.find({ query: '', limit: 1 })
    return sample.totalCount || 0
  }

  // Deprecated: Remove methods that load everything
  // These are kept for backward compatibility but should not be used
  private async _getAllItemIds(): Promise<string[]> {
    console.warn('⚠️ _getAllItemIds() is deprecated and will fail with large datasets. Use _iterateAllItems() or _getSampleItemIds() instead.')
    return this._getSampleItemIds(10000)  // Return sample only
  }

  // ===== GRAPH ALGORITHM SUPPORTING METHODS =====
  
  private _calculateTotalWeight(edges: Map<string, Map<string, number>>): number {
    let total = 0
    for (const edgeMap of edges.values()) {
      for (const weight of edgeMap.values()) {
        total += weight
      }
    }
    return total / 2 // Undirected graph, so divide by 2
  }
  
  private _getNeighborCommunities(
    node: string, 
    edges: Map<string, Map<string, number>>, 
    communities: Map<string, number>
  ): Set<number> {
    const neighborCommunities = new Set<number>()
    const nodeEdges = edges.get(node)
    
    if (nodeEdges) {
      for (const neighbor of nodeEdges.keys()) {
        const neighborCommunity = communities.get(neighbor)
        if (neighborCommunity !== undefined) {
          neighborCommunities.add(neighborCommunity)
        }
      }
    }
    
    return neighborCommunities
  }
  
  private _calculateModularityGain(
    node: string,
    oldCommunity: number,
    newCommunity: number,
    edges: Map<string, Map<string, number>>,
    communities: Map<string, number>,
    totalWeight: number
  ): number {
    // Calculate the degree of the node
    const nodeDegree = this._getNodeDegree(node, edges)
    
    // Calculate edges to old and new communities
    const edgesToOld = this._getEdgesToCommunity(node, oldCommunity, edges, communities)
    const edgesToNew = this._getEdgesToCommunity(node, newCommunity, edges, communities)
    
    // Calculate community weights
    const oldCommunityWeight = this._getCommunityWeight(oldCommunity, edges, communities)
    const newCommunityWeight = this._getCommunityWeight(newCommunity, edges, communities)
    
    // Modularity gain calculation (simplified)
    const oldContrib = edgesToOld - (nodeDegree * oldCommunityWeight) / (2 * totalWeight)
    const newContrib = edgesToNew - (nodeDegree * newCommunityWeight) / (2 * totalWeight)
    
    return newContrib - oldContrib
  }
  
  private _getNodeDegree(node: string, edges: Map<string, Map<string, number>>): number {
    const nodeEdges = edges.get(node)
    if (!nodeEdges) return 0
    
    return Array.from(nodeEdges.values()).reduce((sum, weight) => sum + weight, 0)
  }
  
  private _getEdgesToCommunity(
    node: string,
    community: number,
    edges: Map<string, Map<string, number>>,
    communities: Map<string, number>
  ): number {
    const nodeEdges = edges.get(node)
    if (!nodeEdges) return 0
    
    let total = 0
    for (const [neighbor, weight] of nodeEdges) {
      if (communities.get(neighbor) === community) {
        total += weight
      }
    }
    return total
  }
  
  private _getCommunityWeight(
    community: number,
    edges: Map<string, Map<string, number>>,
    communities: Map<string, number>
  ): number {
    let total = 0
    for (const [node, nodeCommunity] of communities) {
      if (nodeCommunity === community) {
        total += this._getNodeDegree(node, edges)
      }
    }
    return total
  }
  
  private _calculateCommunityModularity(
    members: string[],
    edges: Map<string, Map<string, number>>,
    totalWeight: number
  ): number {
    if (members.length < 2) return 0
    
    let internalWeight = 0
    let totalDegree = 0
    
    for (const member of members) {
      const memberEdges = edges.get(member)
      if (memberEdges) {
        totalDegree += Array.from(memberEdges.values()).reduce((sum, w) => sum + w, 0)
        
        // Count internal edges
        for (const [neighbor, weight] of memberEdges) {
          if (members.includes(neighbor)) {
            internalWeight += weight
          }
        }
      }
    }
    
    internalWeight /= 2 // Undirected graph
    const expectedInternal = (totalDegree * totalDegree) / (4 * totalWeight)
    
    return (internalWeight / totalWeight) - expectedInternal / totalWeight
  }
  
  private _calculateCommunityDensity(
    members: string[],
    edges: Map<string, Map<string, number>>
  ): number {
    if (members.length < 2) return 0
    
    let actualEdges = 0
    const maxPossibleEdges = (members.length * (members.length - 1)) / 2
    
    for (const member of members) {
      const memberEdges = edges.get(member)
      if (memberEdges) {
        for (const neighbor of memberEdges.keys()) {
          if (members.includes(neighbor) && member < neighbor) { // Avoid double counting
            actualEdges++
          }
        }
      }
    }
    
    return actualEdges / maxPossibleEdges
  }
  
  private _findStrongestConnections(
    members: string[],
    edges: Map<string, Map<string, number>>,
    limit: number
  ): Array<{from: string, to: string, weight: number}> {
    const connections: Array<{from: string, to: string, weight: number}> = []
    
    for (const member of members) {
      const memberEdges = edges.get(member)
      if (memberEdges) {
        for (const [neighbor, weight] of memberEdges) {
          if (members.includes(neighbor) && member < neighbor) { // Avoid duplicates
            connections.push({ from: member, to: neighbor, weight })
          }
        }
      }
    }
    
    return connections
      .sort((a, b) => b.weight - a.weight)
      .slice(0, limit)
  }

  // ===== K-MEANS UTILITIES =====
  
  /**
   * Get items with their vector representations
   */
  private async _getItemsWithVectors(itemIds: string[]): Promise<Array<{id: string, vector: number[]}>> {
    const items = await Promise.all(
      itemIds.map(async id => {
        const noun = await this.brain.get(id)
        return {
          id,
          vector: noun?.vector || []
        }
      })
    )
    
    return items.filter((item): item is {id: string, vector: number[]} => 
      item !== null && item.vector.length > 0
    )
  }
  
  /**
   * Calculate centroid from items using existing distance functions
   */
  private async _calculateCentroidFromItems(items: Array<{vector: number[]}>): Promise<number[]> {
    if (items.length === 0) return []
    if (items.length === 1) return [...items[0].vector]
    
    const dimensions = items[0].vector.length
    const centroid = new Array(dimensions).fill(0)
    
    for (const item of items) {
      for (let i = 0; i < dimensions; i++) {
        centroid[i] += item.vector[i]
      }
    }
    
    for (let i = 0; i < dimensions; i++) {
      centroid[i] /= items.length
    }
    
    return centroid
  }
  
  /**
   * Initialize centroids using k-means++ algorithm for better convergence
   */
  private async _initializeCentroidsKMeansPlusPlus(
    items: Array<{id: string, vector: number[]}>, 
    k: number
  ): Promise<number[][]> {
    const centroids: number[][] = []
    
    // Choose first centroid randomly
    const firstIdx = Math.floor(Math.random() * items.length)
    centroids.push([...items[firstIdx].vector])
    
    // Choose remaining centroids using k-means++ probability
    for (let i = 1; i < k; i++) {
      const distances = items.map(item => {
        // Find distance to closest existing centroid
        let minDist = Infinity
        for (const centroid of centroids) {
          const dist = this._calculateSquaredDistance(item.vector, centroid)
          minDist = Math.min(minDist, dist)
        }
        return minDist
      })
      
      // Choose next centroid with probability proportional to squared distance
      const totalDistance = distances.reduce((sum, d) => sum + d, 0)
      const target = Math.random() * totalDistance
      
      let cumulative = 0
      for (let j = 0; j < distances.length; j++) {
        cumulative += distances[j]
        if (cumulative >= target) {
          centroids.push([...items[j].vector])
          break
        }
      }
    }
    
    return centroids
  }
  
  /**
   * Assign points to nearest centroids using existing distance functions
   */
  private async _assignPointsToCentroids(
    items: Array<{id: string, vector: number[]}>,
    centroids: number[][]
  ): Promise<number[]> {
    const assignments: number[] = []
    
    for (const item of items) {
      let bestCentroid = 0
      let minDistance = Infinity
      
      for (let i = 0; i < centroids.length; i++) {
        const distance = this._calculateSquaredDistance(item.vector, centroids[i])
        if (distance < minDistance) {
          minDistance = distance
          bestCentroid = i
        }
      }
      
      assignments.push(bestCentroid)
    }
    
    return assignments
  }
  
  /**
   * Update centroids based on current assignments
   */
  private async _updateCentroids(
    items: Array<{id: string, vector: number[]}>,
    assignments: number[],
    k: number
  ): Promise<number[][]> {
    const newCentroids: number[][] = []
    
    for (let i = 0; i < k; i++) {
      const clusterItems = items.filter((_, idx) => assignments[idx] === i)
      
      if (clusterItems.length > 0) {
        newCentroids.push(await this._calculateCentroidFromItems(clusterItems))
      } else {
        // Keep old centroid if no items assigned
        newCentroids.push(new Array(items[0].vector.length).fill(0))
      }
    }
    
    return newCentroids
  }
  
  /**
   * Calculate how much assignments have changed between iterations
   */
  private _calculateAssignmentChangeRate(oldAssignments: number[], newAssignments: number[]): number {
    if (oldAssignments.length !== newAssignments.length) return 1.0
    
    let changes = 0
    for (let i = 0; i < oldAssignments.length; i++) {
      if (oldAssignments[i] !== newAssignments[i]) {
        changes++
      }
    }
    
    return changes / oldAssignments.length
  }
  
  /**
   * Calculate cluster confidence for k-means clusters
   */
  private async _calculateKMeansClusterConfidence(
    clusterItems: Array<{vector: number[]}>,
    centroid: number[]
  ): Promise<number> {
    if (clusterItems.length <= 1) return 1.0
    
    // Calculate average distance to centroid
    const distances = clusterItems.map(item => 
      this._calculateSquaredDistance(item.vector, centroid)
    )
    const avgDistance = distances.reduce((sum, d) => sum + d, 0) / distances.length
    
    // Calculate standard deviation
    const variance = distances.reduce((sum, d) => sum + Math.pow(d - avgDistance, 2), 0) / distances.length
    const stdDev = Math.sqrt(variance)
    
    // Higher confidence for tighter clusters
    const tightness = avgDistance > 0 ? Math.max(0, 1 - (stdDev / avgDistance)) : 1.0
    
    return Math.min(1.0, tightness)
  }
  
  // ===== DBSCAN UTILITIES =====
  
  /**
   * Estimate optimal eps parameter using k-nearest neighbor distances
   */
  private async _estimateOptimalEps(
    items: Array<{id: string, vector: number[]}>,
    minPts: number
  ): Promise<number> {
    if (items.length < minPts) return 0.5
    
    // Calculate k-nearest neighbor distances for each point
    const kDistances: number[] = []
    
    for (const item of items) {
      const distances: number[] = []
      
      for (const otherItem of items) {
        if (item.id !== otherItem.id) {
          const distance = Math.sqrt(this._calculateSquaredDistance(item.vector, otherItem.vector))
          distances.push(distance)
        }
      }
      
      distances.sort((a, b) => a - b)
      
      // Get k-th nearest neighbor distance (minPts-1 because we exclude self)
      const kthDistance = distances[Math.min(minPts - 1, distances.length - 1)]
      kDistances.push(kthDistance)
    }
    
    kDistances.sort((a, b) => a - b)
    
    // Use knee point detection - find point with maximum curvature
    // Simplified approach: use 90th percentile of k-distances
    const percentileIndex = Math.floor(kDistances.length * 0.9)
    return kDistances[percentileIndex] || 0.5
  }
  
  /**
   * Find neighbors within epsilon distance using efficient vector operations
   */
  private async _findNeighborsWithinEps(
    item: {id: string, vector: number[]},
    allItems: Array<{id: string, vector: number[]}>,
    eps: number
  ): Promise<Array<{id: string, vector: number[]}>> {
    const neighbors: Array<{id: string, vector: number[]}> = []
    const epsSquared = eps * eps
    
    for (const otherItem of allItems) {
      if (item.id !== otherItem.id) {
        const distanceSquared = this._calculateSquaredDistance(item.vector, otherItem.vector)
        if (distanceSquared <= epsSquared) {
          neighbors.push(otherItem)
        }
      }
    }
    
    return neighbors
  }
  
  /**
   * Expand DBSCAN cluster by adding density-reachable points
   */
  private async _expandCluster(
    seedPoint: {id: string, vector: number[]},
    neighbors: Array<{id: string, vector: number[]}>,
    clusterId: number,
    eps: number,
    minPts: number,
    allItems: Array<{id: string, vector: number[]}>,
    visited: Map<string, boolean>,
    clusterAssignments: Map<string, number>
  ): Promise<void> {
    clusterAssignments.set(seedPoint.id, clusterId)
    
    let i = 0
    while (i < neighbors.length) {
      const neighbor = neighbors[i]
      
      if (!visited.get(neighbor.id)) {
        visited.set(neighbor.id, true)
        
        const neighborNeighbors = await this._findNeighborsWithinEps(neighbor, allItems, eps)
        
        if (neighborNeighbors.length >= minPts) {
          // Add new neighbors to the list (union operation)
          for (const newNeighbor of neighborNeighbors) {
            if (!neighbors.some(n => n.id === newNeighbor.id)) {
              neighbors.push(newNeighbor)
            }
          }
        }
      }
      
      // If neighbor is not assigned to any cluster, assign to current cluster
      if (!clusterAssignments.has(neighbor.id)) {
        clusterAssignments.set(neighbor.id, clusterId)
      }
      
      i++
    }
  }
  
  /**
   * Calculate DBSCAN cluster confidence based on density
   */
  private async _calculateDBSCANClusterConfidence(
    clusterItems: Array<{vector: number[]}>,
    eps: number
  ): Promise<number> {
    if (clusterItems.length <= 1) return 1.0
    
    // Calculate average density within the cluster
    let totalNeighborCount = 0
    const epsSquared = eps * eps
    
    for (const item of clusterItems) {
      let neighborCount = 0
      
      for (const otherItem of clusterItems) {
        if (item !== otherItem) {
          const distanceSquared = this._calculateSquaredDistance(item.vector, otherItem.vector)
          if (distanceSquared <= epsSquared) {
            neighborCount++
          }
        }
      }
      
      totalNeighborCount += neighborCount
    }
    
    const avgDensity = totalNeighborCount / clusterItems.length
    const maxPossibleDensity = clusterItems.length - 1
    
    return maxPossibleDensity > 0 ? avgDensity / maxPossibleDensity : 1.0
  }
  
  // ===== VECTOR UTILITIES =====
  
  /**
   * Calculate squared Euclidean distance (more efficient than sqrt)
   */
  private _calculateSquaredDistance(vec1: number[], vec2: number[]): number {
    if (vec1.length !== vec2.length) return Infinity
    
    let sum = 0
    for (let i = 0; i < vec1.length; i++) {
      const diff = vec1[i] - vec2[i]
      sum += diff * diff
    }
    
    return sum
  }
  
  /**
   * Calculate vector coherence for community refinement
   */
  private async _calculateVectorCoherence(items: Array<{vector: number[]}>): Promise<number> {
    if (items.length <= 1) return 1.0
    
    const centroid = await this._calculateCentroidFromItems(items)
    
    // Calculate average distance to centroid
    const distances = items.map(item => Math.sqrt(this._calculateSquaredDistance(item.vector, centroid)))
    const avgDistance = distances.reduce((sum, d) => sum + d, 0) / distances.length
    
    // Calculate cohesion as inverse of average distance (normalized)
    const maxDistance = Math.sqrt(centroid.length) // Rough normalization
    return Math.max(0, 1 - (avgDistance / maxDistance))
  }

  private async _getItemsByField(field: string): Promise<any[]> {
    try {
      // Query all items from brain (limit to reasonable number for clustering)
      const result = await this.brain.find({
        query: '',
        limit: 10000 // Max items for clustering
      })

      if (!result || !Array.isArray(result)) {
        return []
      }

      // Include ALL items for domain clustering - those without the field will be assigned to 'unknown' domain
      const itemsWithField = result.filter((item: any) => {
        // Just ensure item has entity
        return item && item.entity
      })

      // Map to format expected by clustering methods
      return itemsWithField.map((item: any) => {
        const entity = item.entity
        return {
          id: entity.id,
          vector: entity.embedding || entity.vector || [],
          metadata: {
            ...(entity.metadata || {}),
            ...(entity.data || {}),
            // Include root-level fields in metadata for easy access
            noun: entity.type,
            type: entity.type,
            createdAt: entity.createdAt,
            updatedAt: entity.updatedAt,
            label: entity.label
          },
          nounType: entity.type,
          label: entity.label || entity.data || '',
          data: entity.data
        }
      })
    } catch (error) {
      console.error('Error in _getItemsByField:', error)
      return []
    }
  }

  // ===== TRIPLE INTELLIGENCE INTEGRATION =====
  
  /**
   * Generate intelligent cluster labels using Triple Intelligence
   */
  private async _generateIntelligentClusterLabel(
    members: ItemWithMetadata[], 
    algorithm: string
  ): Promise<string> {
    if (members.length === 0) return `${algorithm}-cluster`
    
    // Use simple labeling - Triple Intelligence doesn't generate labels from prompts
    return this._generateClusterLabel(members, algorithm)
  }
  
  /**
   * Generate simple cluster labels based on semantic analysis
   */
  private async _generateClusterLabel(
    members: Array<{id?: string, nounType?: string, label?: string}>, 
    algorithm: string
  ): Promise<string> {
    if (members.length === 0) return `${algorithm}-cluster`
    
    // Analyze member types and create descriptive label
    const typeCount = new Map<string, number>()
    
    for (const member of members) {
      const type = member.nounType || 'unknown'
      typeCount.set(type, (typeCount.get(type) || 0) + 1)
    }
    
    // Find most common type
    let dominantType = 'mixed'
    let maxCount = 0
    
    for (const [type, count] of typeCount) {
      if (count > maxCount) {
        maxCount = count
        dominantType = type
      }
    }
    
    // Generate label based on dominant type and size
    const size = members.length
    const typePercent = Math.round((maxCount / size) * 100)
    
    if (typePercent >= 80) {
      return `${dominantType} group (${size})`
    } else if (typePercent >= 60) {
      return `mostly ${dominantType} (${size})`
    } else {
      const topTypes = Array.from(typeCount.entries())
        .sort((a, b) => b[1] - a[1])
        .slice(0, 2)
        .map(([type]) => type)
        .join(' & ')
      
      return `${topTypes} cluster (${size})`
    }
  }
  
  /**
   * Fuse clustering results using Triple Intelligence consensus
   */
  private async _fuseClusteringResultsWithTripleIntelligence(
    clusterSets: SemanticCluster[][],
    options: ClusteringOptions
  ): Promise<SemanticCluster[]> {
    if (clusterSets.length === 0) return []
    if (clusterSets.length === 1) return clusterSets[0]
    
    // Simple weighted fusion if Triple Intelligence is not available
    const [vectorClusters, graphClusters, semanticClusters] = clusterSets
    
    // Create consensus mapping of items to clusters
    const itemClusterMapping = new Map<string, Array<{algorithm: string, clusterId: string, confidence: number}>>()
    
    // Collect all cluster assignments
    const allAlgorithms = ['vector', 'graph', 'semantic']
    const algorithmClusters = [vectorClusters, graphClusters, semanticClusters]
    
    for (let i = 0; i < algorithmClusters.length; i++) {
      const algorithm = allAlgorithms[i]
      const clusters = algorithmClusters[i] || []
      
      for (const cluster of clusters) {
        for (const memberId of cluster.members) {
          if (!itemClusterMapping.has(memberId)) {
            itemClusterMapping.set(memberId, [])
          }
          
          itemClusterMapping.get(memberId)!.push({
            algorithm,
            clusterId: cluster.id,
            confidence: cluster.confidence
          })
        }
      }
    }
    
    // Find consensus clusters - items that appear together in multiple algorithms
    const consensusClusters = new Map<string, Set<string>>()
    const processedItems = new Set<string>()
    
    for (const [itemId, assignments] of itemClusterMapping) {
      if (processedItems.has(itemId)) continue
      
      // Find all items that consistently cluster with this item
      const consensusGroup = new Set<string>([itemId])
      
      // Look for items that share clusters with this item across algorithms
      for (const assignment of assignments) {
        const sameClusterItems = this._getItemsInCluster(assignment.clusterId, clusterSets)
        
        for (const otherItem of sameClusterItems) {
          if (!processedItems.has(otherItem) && otherItem !== itemId) {
            const otherAssignments = itemClusterMapping.get(otherItem) || []
            
            // Check if items co-occur in multiple algorithms
            const coOccurrences = this._countCoOccurrences(assignments, otherAssignments)
            
            if (coOccurrences >= 2) { // Must appear together in at least 2 algorithms
              consensusGroup.add(otherItem)
            }
          }
        }
      }
      
      // Mark all items in this consensus group as processed
      for (const groupItem of consensusGroup) {
        processedItems.add(groupItem)
      }
      
      if (consensusGroup.size >= (options.minClusterSize || 2)) {
        const consensusId = `fusion-${consensusClusters.size}`
        consensusClusters.set(consensusId, consensusGroup)
      }
    }
    
    // Convert consensus groups to SemanticCluster objects
    const fusedClusters: SemanticCluster[] = []
    
    for (const [clusterId, memberSet] of consensusClusters) {
      const members = Array.from(memberSet)
      const membersWithMetadata = await this._getItemsWithMetadata(members)
      
      if (membersWithMetadata.length > 0) {
        const centroid = await this._calculateCentroidFromItems(membersWithMetadata)
        const label = await this._generateIntelligentClusterLabel(membersWithMetadata, 'multimodal')
        
        // Calculate fusion confidence based on algorithm agreement
        const avgConfidence = this._calculateFusionConfidence(members, itemClusterMapping)
        
        fusedClusters.push({
          id: clusterId,
          centroid,
          members,
          size: members.length,
          confidence: avgConfidence,
          label,
          metadata: {
            clustering: 'multimodal_fusion',
            algorithms: allAlgorithms,
            fusionMethod: 'consensus',
            agreementLevel: avgConfidence
          }
        })
      }
    }
    
    return fusedClusters
  }
  
  /**
   * Get items in a specific cluster from cluster sets
   */
  private _getItemsInCluster(clusterId: string, clusterSets: SemanticCluster[][]): string[] {
    for (const clusterSet of clusterSets) {
      for (const cluster of clusterSet) {
        if (cluster.id === clusterId) {
          return cluster.members
        }
      }
    }
    return []
  }
  
  /**
   * Count co-occurrences between two sets of assignments
   */
  private _countCoOccurrences(
    assignments1: Array<{algorithm: string, clusterId: string, confidence: number}>,
    assignments2: Array<{algorithm: string, clusterId: string, confidence: number}>
  ): number {
    let count = 0
    
    for (const assignment1 of assignments1) {
      for (const assignment2 of assignments2) {
        if (assignment1.algorithm === assignment2.algorithm && 
            assignment1.clusterId === assignment2.clusterId) {
          count++
        }
      }
    }
    
    return count
  }
  
  /**
   * Calculate fusion confidence based on algorithm agreement
   */
  private _calculateFusionConfidence(
    members: string[],
    itemClusterMapping: Map<string, Array<{algorithm: string, clusterId: string, confidence: number}>>
  ): number {
    let totalConfidence = 0
    let totalAssignments = 0
    
    for (const member of members) {
      const assignments = itemClusterMapping.get(member) || []
      
      for (const assignment of assignments) {
        totalConfidence += assignment.confidence
        totalAssignments++
      }
    }
    
    return totalAssignments > 0 ? totalConfidence / totalAssignments : 0.5
  }

  // ===== ADDITIONAL UTILITIES =====
  
  /**
   * Generate empty clustering result for edge cases
   */
  private _createEmptyResult(startTime: number, algorithm: string): ClusteringResult {
    return {
      clusters: [],
      metrics: this._createPerformanceMetrics(startTime, 0, algorithm),
      metadata: {
        totalItems: 0,
        clustersFound: 0,
        averageClusterSize: 0,
        timestamp: new Date()
      }
    }
  }

  // ===== SAMPLING AND PROJECTION UTILITIES =====
  
  /**
   * Get sample using specified strategy for large dataset clustering
   */
  private async _getSampleUsingStrategy(
    itemIds: string[], 
    sampleSize: number, 
    strategy: 'random' | 'diverse' | 'recent' | 'important'
  ): Promise<string[]> {
    if (itemIds.length <= sampleSize) return itemIds
    
    switch (strategy) {
      case 'random':
        return this._getRandomSample(itemIds, sampleSize)
        
      case 'diverse':
        return await this._getDiverseSample(itemIds, sampleSize)
        
      case 'recent':
        return await this._getRecentSample(itemIds, sampleSize)
        
      case 'important':
        return await this._getImportantSample(itemIds, sampleSize)
        
      default:
        return this._getRandomSample(itemIds, sampleSize)
    }
  }
  
  /**
   * Random sampling
   */
  private _getRandomSample(itemIds: string[], sampleSize: number): string[] {
    const shuffled = [...itemIds].sort(() => Math.random() - 0.5)
    return shuffled.slice(0, sampleSize)
  }
  
  /**
   * Diverse sampling using vector space distribution
   */
  private async _getDiverseSample(itemIds: string[], sampleSize: number): Promise<string[]> {
    // Get vectors for all items
    const itemsWithVectors = await this._getItemsWithVectors(itemIds)
    
    if (itemsWithVectors.length <= sampleSize) {
      return itemIds
    }
    
    // Use k-means++ style selection for diversity
    const sample: string[] = []
    
    // Select first item randomly
    let remainingItems = [...itemsWithVectors]
    const firstIdx = Math.floor(Math.random() * remainingItems.length)
    sample.push(remainingItems[firstIdx].id)
    remainingItems.splice(firstIdx, 1)
    
    // Select remaining items based on maximum distance to already selected items
    while (sample.length < sampleSize && remainingItems.length > 0) {
      let maxDistance = -1
      let bestIdx = 0
      
      for (let i = 0; i < remainingItems.length; i++) {
        const item = remainingItems[i]
        
        // Find minimum distance to any selected item
        let minDistanceToSelected = Infinity
        
        for (const selectedId of sample) {
          const selectedItem = itemsWithVectors.find(it => it.id === selectedId)
          if (selectedItem) {
            const distance = Math.sqrt(this._calculateSquaredDistance(item.vector, selectedItem.vector))
            minDistanceToSelected = Math.min(minDistanceToSelected, distance)
          }
        }
        
        // Select item with maximum minimum distance (most diverse)
        if (minDistanceToSelected > maxDistance) {
          maxDistance = minDistanceToSelected
          bestIdx = i
        }
      }
      
      sample.push(remainingItems[bestIdx].id)
      remainingItems.splice(bestIdx, 1)
    }
    
    return sample
  }
  
  /**
   * Recent sampling based on creation time
   */
  private async _getRecentSample(itemIds: string[], sampleSize: number): Promise<string[]> {
    const items = await Promise.all(
      itemIds.map(async id => {
        const noun = await this.brain.get(id)
        return {
          id,
          createdAt: noun?.createdAt || new Date(0)
        }
      })
    )
    
    // Sort by creation time (most recent first)
    items.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
    
    return items.slice(0, sampleSize).map(item => item.id)
  }
  
  /**
   * Important sampling based on connection count and metadata
   */
  private async _getImportantSample(itemIds: string[], sampleSize: number): Promise<string[]> {
    const items = await Promise.all(
      itemIds.map(async id => {
        const verbs = await this.brain.getRelations(id)
        const noun = await this.brain.get(id)
        
        // Calculate importance score
        const connectionScore = verbs.length
        const dataScore = noun?.data ? Object.keys(noun.data).length : 0
        const importanceScore = connectionScore * 2 + dataScore
        
        return {
          id,
          importance: importanceScore
        }
      })
    )
    
    // Sort by importance (highest first)
    items.sort((a, b) => b.importance - a.importance)
    
    return items.slice(0, sampleSize).map(item => item.id)
  }
  
  /**
   * Project clusters back to full dataset using HNSW neighbors
   */
  private async _projectClustersToFullDataset(
    sampleClusters: SemanticCluster[],
    fullItemIds: string[],
    sampleIds: string[]
  ): Promise<SemanticCluster[]> {
    const projectedClusters: SemanticCluster[] = []
    
    // Create mapping of items not in sample
    const remainingItems = fullItemIds.filter(id => !sampleIds.includes(id))
    
    // For each sample cluster, find which remaining items should belong to it
    for (const sampleCluster of sampleClusters) {
      const projectedMembers = [...sampleCluster.members]
      
      // For each remaining item, find its nearest neighbors in the sample
      for (const itemId of remainingItems) {
        try {
          const neighbors = await this.brain.neural.neighbors(itemId, { 
            limit: 3,
            includeMetadata: false 
          })
          
          // Check if any of the nearest neighbors belong to this cluster
          let belongsToCluster = false
          for (const neighbor of neighbors.neighbors) {
            if (sampleCluster.members.includes(neighbor.id) && neighbor.similarity > 0.7) {
              belongsToCluster = true
              break
            }
          }
          
          if (belongsToCluster) {
            projectedMembers.push(itemId)
          }
        } catch (error) {
          // Skip items that can't be processed
          continue
        }
      }
      
      // Create projected cluster
      if (projectedMembers.length > 0) {
        const membersWithVectors = await this._getItemsWithVectors(projectedMembers)
        
        projectedClusters.push({
          ...sampleCluster,
          id: `projected-${sampleCluster.id}`,
          members: projectedMembers,
          size: projectedMembers.length,
          centroid: await this._calculateCentroidFromItems(membersWithVectors),
          confidence: sampleCluster.confidence * 0.9, // Slightly lower confidence for projection
          metadata: {
            ...sampleCluster.metadata,
            isProjected: true,
            originalSampleSize: sampleCluster.size,
            projectedSize: projectedMembers.length
          }
        })
      }
    }
    
    return projectedClusters
  }

  private _groupByDomain(items: any[], field: string): Map<string, any[]> {
    const groups = new Map()
    for (const item of items) {
      // Check multiple locations for the field value
      let domain: any = 'unknown'

      // Special handling for type/nounType field
      if (field === 'type' || field === 'nounType') {
        domain = item.nounType || item.metadata?.noun || item.metadata?.type || 'unknown'
      } else {
        // Check root level first
        domain = item[field]

        // Then check metadata
        if (domain == null) {
          domain = item.metadata?.[field]
        }

        // Then check data
        if (domain == null) {
          domain = item.data?.[field]
        }

        // Fallback to unknown
        if (domain == null) {
          domain = 'unknown'
        }
      }

      // Convert domain to string for Map key
      const domainKey = String(domain)

      if (!groups.has(domainKey)) {
        groups.set(domainKey, [])
      }
      groups.get(domainKey).push(item)
    }
    return groups
  }

  private _calculateDomainConfidence(cluster: SemanticCluster, domainItems: any[]): number {
    // Calculate how well this cluster represents the domain
    // Based on cluster density and coherence
    const density = cluster.members.length / (cluster.members.length + 10) // Normalize
    const coherence = cluster.cohesion || 0.5 // Use cluster's cohesion if available
    
    // Domain relevance: what fraction of cluster members are from this domain
    const domainMemberCount = cluster.members.filter(id => 
      domainItems.some(item => item.id === id)
    ).length
    const domainRelevance = cluster.members.length > 0 
      ? domainMemberCount / cluster.members.length 
      : 0
    
    return (density * 0.3 + coherence * 0.3 + domainRelevance * 0.4) // Weighted average
  }

  private async _findCrossDomainMembers(cluster: SemanticCluster, threshold: number): Promise<string[]> {
    try {
      // Find cluster members that have high similarity to items in other domains
      const crossDomainMembers: string[] = []

      for (const memberId of cluster.members) {
        try {
          // Get neighbors for this member
          const neighbors = await this.neighbors(memberId, {
            limit: 10,
            minSimilarity: threshold
          })

          if (Array.isArray(neighbors) && neighbors.length > 0) {
            // Check if any neighbors are NOT in this cluster
            const hasExternalNeighbors = neighbors.some(neighbor =>
              !cluster.members.includes(typeof neighbor === 'object' ? neighbor.id : neighbor)
            )

            if (hasExternalNeighbors) {
              crossDomainMembers.push(memberId)
            }
          }
        } catch (error) {
          // Skip members that can't be processed
          continue
        }
      }

      return crossDomainMembers
    } catch (error) {
      console.error('Error in _findCrossDomainMembers:', error)
      return []
    }
  }

  private async _findCrossDomainClusters(clusters: DomainCluster[], threshold: number): Promise<DomainCluster[]> {
    try {
      const crossDomainClusters: DomainCluster[] = []

      // Group clusters by domain
      const domainMap = new Map<string, DomainCluster[]>()
      for (const cluster of clusters) {
        const domain = cluster.domain || 'unknown'
        if (!domainMap.has(domain)) {
          domainMap.set(domain, [])
        }
        domainMap.get(domain)!.push(cluster)
      }

      // Find clusters with high inter-domain similarity
      const domains = Array.from(domainMap.keys())
      for (let i = 0; i < domains.length; i++) {
        for (let j = i + 1; j < domains.length; j++) {
          const domain1 = domains[i]
          const domain2 = domains[j]
          const clusters1 = domainMap.get(domain1)!
          const clusters2 = domainMap.get(domain2)!

          // Compare clusters between domains
          for (const c1 of clusters1) {
            for (const c2 of clusters2) {
              try {
                // Calculate similarity between cluster centroids
                if (!c1.centroid || !c2.centroid || c1.centroid.length === 0 || c2.centroid.length === 0) {
                  continue
                }

                const similarity = 1 - cosineDistance(
                  Array.from(c1.centroid) as number[],
                  Array.from(c2.centroid) as number[]
                )

                if (similarity >= threshold) {
                  // Create a cross-domain cluster
                  const mergedMembers = [...new Set([...c1.members, ...c2.members])]
                  const mergedCentroid = this._averageVectors([
                    Array.from(c1.centroid) as number[],
                    Array.from(c2.centroid) as number[]
                  ])

                  crossDomainClusters.push({
                    ...c1,
                    id: `cross-${domain1}-${domain2}-${crossDomainClusters.length}`,
                    label: `Cross-domain: ${c1.label} + ${c2.label}`,
                    members: mergedMembers,
                    centroid: mergedCentroid,
                    domain: `${domain1}+${domain2}`,
                    domainConfidence: similarity,
                    crossDomainMembers: mergedMembers
                  })
                }
              } catch (error) {
                // Skip cluster pairs that can't be compared
                continue
              }
            }
          }
        }
      }

      return crossDomainClusters
    } catch (error) {
      console.error('Error in _findCrossDomainClusters:', error)
      return []
    }
  }

  private _averageVectors(vectors: number[][]): number[] {
    if (vectors.length === 0) return []
    if (vectors.length === 1) return [...vectors[0]]

    const dim = vectors[0].length
    const result = new Array(dim).fill(0)

    for (const vector of vectors) {
      for (let i = 0; i < dim; i++) {
        result[i] += vector[i]
      }
    }

    for (let i = 0; i < dim; i++) {
      result[i] /= vectors.length
    }

    return result
  }

  private async _getItemsByTimeWindow(timeField: string, window: TimeWindow): Promise<any[]> {
    try {
      // Query all items from brain
      const result = await this.brain.find({
        query: '',
        limit: 10000 // Max items for clustering
      })

      if (!result || !Array.isArray(result)) {
        return []
      }

      // Filter items within the time window
      const itemsInWindow = result.filter((item: any) => {
        if (!item || !item.entity) return false

        const entity = item.entity

        // Get timestamp value from various possible locations
        let timestamp: any = null

        // Check root level first
        if (timeField === 'createdAt' || timeField === 'updatedAt') {
          timestamp = entity[timeField]
        }

        // Check metadata/data
        if (timestamp == null) {
          timestamp = entity.metadata?.[timeField] || entity.data?.[timeField]
        }

        if (timestamp == null) {
          return false
        }

        // Convert to Date if needed
        const itemDate = timestamp instanceof Date ? timestamp : new Date(timestamp)

        if (isNaN(itemDate.getTime())) {
          return false // Invalid date
        }

        // Check if item falls within window
        return itemDate >= window.start && itemDate <= window.end
      })

      // Map to format expected by clustering methods
      return itemsInWindow.map((item: any) => {
        const entity = item.entity
        return {
          id: entity.id,
          vector: entity.embedding || entity.vector || [],
          metadata: {
            ...(entity.metadata || {}),
            ...(entity.data || {}),
            noun: entity.noun,
            type: entity.noun,
            createdAt: entity.createdAt,
            updatedAt: entity.updatedAt,
            label: entity.label
          },
          nounType: entity.noun,
          label: entity.label || entity.data || '',
          data: entity.data
        }
      })
    } catch (error) {
      console.error('Error in _getItemsByTimeWindow:', error)
      return []
    }
  }

  private async _calculateTemporalMetrics(cluster: SemanticCluster, items: any[], timeField: string): Promise<any> {
    // Calculate temporal characteristics of the cluster
    return {
      trend: 'stable' as const,
      metrics: {
        startTime: new Date(),
        endTime: new Date(),
        peakTime: new Date(),
        frequency: 1
      }
    }
  }

  private _mergeOverlappingTemporalClusters(clusters: TemporalCluster[]): TemporalCluster[] {
    // Merge clusters from overlapping time windows
    return clusters
  }

  private _adjustThresholdAdaptively(clusters: SemanticCluster[], currentThreshold: number | undefined): number {
    // Adjust clustering threshold based on results
    return currentThreshold || 0.6
  }

  private async _calculateItemToClusterSimilarity(itemId: string, cluster: SemanticCluster): Promise<number> {
    // Calculate similarity between an item and a cluster centroid
    const item = await this.brain.get(itemId)
    if (!item || !item.vector || !cluster.centroid) {
      return 0 // No similarity if vectors missing
    }
    
    // Calculate cosine similarity
    const dotProduct = item.vector.reduce((sum: number, val: number, i: number) => sum + val * (cluster.centroid as number[])[i], 0)
    const itemMagnitude = Math.sqrt(item.vector.reduce((sum: number, val: number) => sum + val * val, 0))
    const centroidMagnitude = Math.sqrt((cluster.centroid as number[]).reduce((sum: number, val: number) => sum + val * val, 0))
    
    if (itemMagnitude === 0 || centroidMagnitude === 0) {
      return 0
    }
    
    return dotProduct / (itemMagnitude * centroidMagnitude)
  }

  private async _recalculateClusterCentroid(cluster: SemanticCluster): Promise<Vector> {
    // Recalculate centroid after adding new members
    if (cluster.members.length === 0) {
      return cluster.centroid as Vector // Keep existing if no members
    }
    
    // Get all member vectors
    const memberVectors: Vector[] = []
    for (const memberId of cluster.members) {
      const member = await this.brain.get(memberId)
      if (member && member.vector) {
        memberVectors.push(member.vector)
      }
    }
    
    if (memberVectors.length === 0) {
      return cluster.centroid as Vector // Keep existing if no valid vectors
    }
    
    // Calculate mean vector (centroid)
    const dimensions = memberVectors[0].length
    const newCentroid = new Array(dimensions).fill(0)
    
    for (const vector of memberVectors) {
      for (let i = 0; i < dimensions; i++) {
        newCentroid[i] += vector[i]
      }
    }
    
    for (let i = 0; i < dimensions; i++) {
      newCentroid[i] /= memberVectors.length
    }
    
    return newCentroid
  }

  private async _calculateSimilarity(id1: string, id2: string): Promise<number> {
    return await this.similar(id1, id2) as number
  }

  private _calculateEdgeWeight(verb: any): number {
    // Calculate edge weight based on verb properties
    let weight = 1.0
    
    // Factor in connection strength if available
    if (verb.connections && verb.connections instanceof Map) {
      const connectionCount = verb.connections.size
      weight += Math.log(connectionCount + 1) * 0.1
    }
    
    // Factor in verb type significance
    const significantVerbs = ['caused', 'created', 'contains', 'implements', 'extends']
    if (verb.verb && significantVerbs.includes(verb.verb.toLowerCase())) {
      weight += 0.3
    }
    
    // Factor in recency if available
    if (verb.metadata?.createdAt) {
      const now = Date.now()
      const created = new Date(verb.metadata.createdAt).getTime()
      const daysSinceCreated = (now - created) / (1000 * 60 * 60 * 24)
      // Newer relationships get slight boost
      weight += Math.max(0, (30 - daysSinceCreated) / 100)
    }
    
    return Math.min(weight, 3.0) // Cap at 3.0
  }

  private _sortNeighbors(neighbors: Neighbor[], sortBy: 'similarity' | 'importance' | 'recency'): void {
    switch (sortBy) {
      case 'similarity':
        neighbors.sort((a, b) => b.similarity - a.similarity)
        break
      case 'importance':
        neighbors.sort((a, b) => (b.metadata?.importance || 0) - (a.metadata?.importance || 0))
        break
      case 'recency':
        neighbors.sort((a, b) => {
          const aTime = new Date(a.metadata?.createdAt || 0).getTime()
          const bTime = new Date(b.metadata?.createdAt || 0).getTime()
          return bTime - aTime
        })
        break
    }
  }

  private async _buildSemanticHierarchy(item: any, options: HierarchyOptions): Promise<SemanticHierarchy> {
    // Build semantic hierarchy around an item
    // Return structure expected by tests: { root, levels }
    return {
      root: {
        id: item.id,
        vector: item.vector,
        metadata: item.metadata
      },
      levels: []
    }
  }

  private async _detectOutliersClusterBased(threshold: number, options: OutlierOptions): Promise<Outlier[]> {
    // Detect outliers using cluster-based method
    return []
  }

  private async _detectOutliersIsolation(threshold: number, options: OutlierOptions): Promise<Outlier[]> {
    // Detect outliers using isolation forest method
    return []
  }

  private async _detectOutliersStatistical(threshold: number, options: OutlierOptions): Promise<Outlier[]> {
    // Detect outliers using statistical methods
    return []
  }

  private async _generateVisualizationNodes(maxNodes: number, options: VisualizationOptions): Promise<any[]> {
    // Generate nodes for visualization
    return []
  }

  private async _generateVisualizationEdges(nodes: any[], options: VisualizationOptions): Promise<any[]> {
    // Generate edges for visualization
    return []
  }

  private async _generateVisualizationClusters(nodes: any[]): Promise<any[]> {
    // Generate cluster information for visualization
    return []
  }

  private async _applyLayoutAlgorithm(nodes: any[], edges: any[], algorithm: string, dimensions: number): Promise<any[]> {
    // Apply layout algorithm to position nodes
    return nodes.map((node, i) => ({
      ...node,
      x: Math.random() * 100,
      y: Math.random() * 100,
      z: dimensions === 3 ? Math.random() * 100 : undefined
    }))
  }

  private _manhattanDistance(v1: Vector, v2: Vector): number {
    let sum = 0
    for (let i = 0; i < v1.length; i++) {
      sum += Math.abs(v1[i] - v2[i])
    }
    return sum
  }

  private _calculateConfidence(score: number, v1: Vector, v2: Vector): number {
    // Calculate confidence based on vector magnitudes and score
    return Math.min(1, score + 0.1)
  }

  private _generateSimilarityExplanation(score: number, metric: string): string {
    if (score > 0.9) return `Very high similarity using ${metric} distance`
    if (score > 0.7) return `High similarity using ${metric} distance`
    if (score > 0.5) return `Moderate similarity using ${metric} distance`
    if (score > 0.3) return `Low similarity using ${metric} distance`
    return `Very low similarity using ${metric} distance`
  }

  // ===== PUBLIC API: UTILITY & STATUS =====

  /**
   * Get performance metrics for monitoring
   */
  getPerformanceMetrics(operation?: string): Map<string, PerformanceMetrics[]> | PerformanceMetrics[] {
    if (operation) {
      return this.performanceMetrics.get(operation) || []
    }
    return this.performanceMetrics
  }

  /**
   * Clear all caches
   */
  clearCaches(): void {
    this.similarityCache.clear()
    this.clusterCache.clear()
    this.hierarchyCache.clear()
    this.neighborsCache.clear()
  }

  /**
   * Get cache statistics
   */
  getCacheStats(): Record<string, { size: number; maxSize: number }> {
    const maxSize = this.config.cacheSize || 1000
    return {
      similarity: { size: this.similarityCache.size, maxSize },
      clustering: { size: this.clusterCache.size, maxSize },
      hierarchy: { size: this.hierarchyCache.size, maxSize },
      neighbors: { size: this.neighborsCache.size, maxSize }
    }
  }

  // ===== MISSING HELPER METHODS =====
  
  /**
   * Analyze data characteristics for algorithm selection
   */
  private async _analyzeDataCharacteristics(itemIds: string[]): Promise<{
    size: number,
    dimensionality: number,
    graphDensity: number,
    typeDistribution: Record<string, number>
  }> {
    const size = itemIds.length
    const items = await this._getItemsWithMetadata(itemIds.slice(0, Math.min(100, size)))
    
    const dimensionality = items.length > 0 ? items[0].vector.length : 0
    
    // Calculate graph density by sampling verb relationships
    let connectionCount = 0
    const sampleSize = Math.min(50, itemIds.length)
    for (let i = 0; i < sampleSize; i++) {
      try {
        const verbs = await this.brain.getVerbsForNoun(itemIds[i])
        connectionCount += verbs.length
      } catch (error) {
        // Skip items that can't be processed
        continue
      }
    }
    const graphDensity = sampleSize > 0 ? connectionCount / (sampleSize * sampleSize) : 0
    
    // Calculate type distribution
    const typeDistribution: Record<string, number> = {}
    for (const item of items) {
      const type = item.nounType
      typeDistribution[type] = (typeDistribution[type] || 0) + 1
    }
    
    return { size, dimensionality, graphDensity, typeDistribution }
  }
  
  /**
   * Calculate centroid for a group of items
   */
  private async _calculateGroupCentroid(items: ItemWithMetadata[]): Promise<number[]> {
    return this._calculateCentroidFromItems(items)
  }
  
  /**
   * Cluster within semantic type using vector similarity
   */
  private async _clusterWithinSemanticType(
    items: ItemWithMetadata[], 
    options: ClusteringOptions
  ): Promise<SemanticCluster[]> {
    if (items.length <= 2) {
      return [{
        id: `semantic-single-${items[0]?.nounType || 'unknown'}`,
        centroid: await this._calculateCentroidFromItems(items),
        members: items.map(item => item.id),
        size: items.length,
        confidence: 1.0,
        label: `${items[0]?.nounType || 'unknown'} group`,
        metadata: { clustering: 'semantic', nounType: items[0]?.nounType }
      }]
    }
    
    // Use hierarchical clustering for within-type clustering
    const result = await this._performHierarchicalClustering(
      items.map(item => item.id), 
      { ...options, maxClusters: Math.min(Math.ceil(items.length / 3), 10) }
    )
    return result.clusters
  }
  
  /**
   * Find cross-type connections via verbs
   */
  private async _findCrossTypeConnections(
    typeGroups: Map<string, ItemWithMetadata[]>,
    _options: ClusteringOptions
  ): Promise<Array<{from: string, to: string, strength: number}>> {
    const connections: Array<{from: string, to: string, strength: number}> = []
    
    // Convert Map to array for compatibility
    const typeGroupsArray = Array.from(typeGroups.entries())
    
    for (const [fromType, fromItems] of typeGroupsArray) {
      for (const [toType, toItems] of typeGroupsArray) {
        if (fromType !== toType) {
          for (const fromItem of fromItems.slice(0, 10)) { // Sample to avoid N^2
            try {
              const verbs = await this.brain.getVerbsForNoun(fromItem.id)
              
              for (const verb of verbs) {
                const toItem = toItems.find(item => item.id === verb.target)
                if (toItem) {
                  connections.push({
                    from: fromItem.id,
                    to: toItem.id,
                    strength: verb.confidence || 0.7
                  })
                }
              }
            } catch (error) {
              // Skip items that can't be processed
              continue
            }
          }
        }
      }
    }
    
    return connections.filter(conn => conn.strength > 0.5)
  }
  
  /**
   * Merge semantic clusters based on connections
   */
  private async _mergeSemanticClusters(
    clusters: SemanticCluster[], 
    connections: Array<{from: string, to: string, strength: number}>
  ): Promise<SemanticCluster[]> {
    // Simple merging based on strong connections
    const merged = [...clusters]
    
    for (const connection of connections) {
      if (connection.strength > 0.8) {
        const fromCluster = merged.find(c => c.members.includes(connection.from))
        const toCluster = merged.find(c => c.members.includes(connection.to))
        
        if (fromCluster && toCluster && fromCluster !== toCluster) {
          // Merge clusters
          fromCluster.members = [...fromCluster.members, ...toCluster.members]
          fromCluster.size = fromCluster.members.length
          fromCluster.label = `merged ${fromCluster.label}`
          
          // Remove merged cluster
          const index = merged.indexOf(toCluster)
          if (index > -1) merged.splice(index, 1)
        }
      }
    }
    
    return merged
  }
  
  /**
   * Get optimal clustering level for HNSW
   */
  private _getOptimalClusteringLevel(totalItems: number): number {
    if (totalItems < 100) return 0
    if (totalItems < 1000) return 1
    if (totalItems < 10000) return 2
    return 3
  }
  
  /**
   * Get nodes at HNSW level
   */
  private async _getHNSWLevelNodes(level: number): Promise<string[]> {
    // This would use the HNSW index to get nodes at specified level
    // For now, return a sample of all items
    const allItems = await this._getAllItemIds()
    const sampleSize = Math.max(10, Math.floor(allItems.length / Math.pow(2, level + 1)))
    return this._getRandomSample(allItems, sampleSize)
  }
  
  /**
   * Find cluster members using HNSW neighbors
   */
  private async _findClusterMembers(
    levelNode: string, 
    _allItems: string[], 
    threshold: number
  ): Promise<string[]> {
    try {
      const neighbors = await this.brain.neural.neighbors(levelNode, { 
        limit: Math.min(50, Math.floor(_allItems.length / 10)),
        minSimilarity: threshold
      })
      
      return [levelNode, ...neighbors.neighbors.map((n: any) => n.id)]
    } catch (error) {
      return [levelNode]
    }
  }
  
  /**
   * Calculate hierarchical clustering confidence
   */
  private async _calculateHierarchicalConfidence(members: string[]): Promise<number> {
    if (members.length <= 1) return 1.0
    
    const items = await this._getItemsWithVectors(members)
    const coherence = await this._calculateVectorCoherence(items)
    
    return coherence
  }
  
  /**
   * Assign unassigned items to nearest clusters
   */
  private async _assignUnassignedItems(
    unassigned: string[], 
    clusters: SemanticCluster[]
  ): Promise<void> {
    for (const itemId of unassigned) {
      if (clusters.length === 0) break
      
      try {
        const noun = await this.brain.get(itemId)
        const itemVector = noun?.vector || []
        if (itemVector.length === 0) continue
        
        let bestCluster = clusters[0]
        let minDistance = Infinity
        
        for (const cluster of clusters) {
          const distance = Math.sqrt(this._calculateSquaredDistance(itemVector as number[], cluster.centroid as number[]))
          if (distance < minDistance) {
            minDistance = distance
            bestCluster = cluster
          }
        }
        
        bestCluster.members.push(itemId)
        bestCluster.size++
      } catch (error) {
        // Skip items that can't be processed
        continue
      }
    }
  }
}