/**
 * LLM Augmentations
 *
 * This file implements cognition augmentations for creating, testing, exporting, and deploying
 * LLM models from the data in Brainy (nouns and verbs).
 */

import {
  AugmentationType,
  ICognitionAugmentation,
  IActivationAugmentation,
  AugmentationResponse
} from '../types/augmentations.js'
import { v4 as uuidv4 } from 'uuid'
import { BrainyData } from '../brainyData.js'
import * as tf from '@tensorflow/tfjs'
import { NounType, VerbType, GraphNoun, GraphVerb } from '../types/graphTypes.js'

/**
 * Interface for LLM model configuration
 */
interface LLMModelConfig {
  name: string
  description?: string
  modelType: 'simple' | 'transformer' | 'custom'
  vocabSize?: number
  embeddingDim?: number
  hiddenDim?: number
  numLayers?: number
  numHeads?: number
  dropoutRate?: number
  maxSequenceLength?: number
  learningRate?: number
  batchSize?: number
  epochs?: number
  customModelPath?: string
}

/**
 * Interface for LLM model metadata
 */
interface LLMModelMetadata {
  id: string
  name: string
  description?: string
  createdAt: Date
  updatedAt: Date
  modelType: 'simple' | 'transformer' | 'custom'
  vocabSize: number
  embeddingDim: number
  trainedOn: {
    nounCount: number
    verbCount: number
    nounTypes: string[]
    verbTypes: string[]
  }
  performance?: {
    accuracy?: number
    loss?: number
    perplexity?: number
  }
  exportFormats?: string[]
}

/**
 * Interface for LLM training options
 */
interface LLMTrainingOptions {
  nounTypes?: NounType[]
  verbTypes?: VerbType[]
  maxSamples?: number
  validationSplit?: number
  includeMetadata?: boolean
  includeEmbeddings?: boolean
  augmentData?: boolean
  earlyStoppingPatience?: number
  epochs?: number
  batchSize?: number
}

/**
 * Interface for LLM testing options
 */
interface LLMTestingOptions {
  testSize?: number
  randomSeed?: number
  metrics?: string[]
  generateSamples?: boolean
  sampleCount?: number
}

/**
 * Interface for LLM export options
 */
interface LLMExportOptions {
  format: 'tfjs' | 'onnx' | 'savedmodel' | 'json'
  quantize?: boolean
  outputPath?: string
  includeMetadata?: boolean
  includeVocab?: boolean
}

/**
 * Interface for LLM deployment options
 */
interface LLMDeploymentOptions {
  target: 'browser' | 'node' | 'cloud'
  cloudProvider?: 'aws' | 'gcp' | 'azure'
  endpoint?: string
  apiKey?: string
  region?: string
  containerize?: boolean
  autoScale?: boolean
  memory?: string
  cpu?: string
}

/**
 * LLMCognitionAugmentation
 *
 * A cognition augmentation that provides functionality for creating, testing, exporting,
 * and deploying LLM models from the data in Brainy.
 */
export class LLMCognitionAugmentation implements ICognitionAugmentation {
  readonly name: string
  readonly description: string = 'Cognition augmentation for LLM model creation and inference'
  enabled: boolean = true
  private isInitialized = false
  private brainyDb: BrainyData | null = null
  private models: Map<string, {
    model: tf.LayersModel,
    metadata: LLMModelMetadata,
    tokenizer?: Map<string, number>
  }> = new Map()
  private defaultModelConfig: LLMModelConfig = {
    name: 'default-llm-model',
    modelType: 'simple',
    vocabSize: 10000,
    embeddingDim: 128,
    hiddenDim: 256,
    numLayers: 2,
    numHeads: 4,
    dropoutRate: 0.1,
    maxSequenceLength: 100,
    learningRate: 0.001,
    batchSize: 32,
    epochs: 10
  }

  // Simplified model presets for easy model creation
  private modelPresets: Record<string, Partial<LLMModelConfig>> = {
    'tiny': {
      name: 'tiny-llm-model',
      modelType: 'simple',
      vocabSize: 3000,
      embeddingDim: 32,
      hiddenDim: 64,
      numLayers: 1,
      dropoutRate: 0.1,
      maxSequenceLength: 50,
      batchSize: 16,
      epochs: 5
    },
    'small': {
      name: 'small-llm-model',
      modelType: 'simple',
      vocabSize: 5000,
      embeddingDim: 64,
      hiddenDim: 128,
      numLayers: 2,
      dropoutRate: 0.1,
      maxSequenceLength: 100,
      batchSize: 32,
      epochs: 10
    },
    'medium': {
      name: 'medium-llm-model',
      modelType: 'transformer',
      vocabSize: 8000,
      embeddingDim: 128,
      hiddenDim: 256,
      numLayers: 3,
      numHeads: 4,
      dropoutRate: 0.1,
      maxSequenceLength: 200,
      batchSize: 32,
      epochs: 15
    },
    'large': {
      name: 'large-llm-model',
      modelType: 'transformer',
      vocabSize: 10000,
      embeddingDim: 256,
      hiddenDim: 512,
      numLayers: 4,
      numHeads: 8,
      dropoutRate: 0.1,
      maxSequenceLength: 300,
      batchSize: 16,
      epochs: 20
    }
  }

  constructor(name: string = 'llm-cognition') {
    this.name = name
  }

  getType(): AugmentationType {
    return AugmentationType.COGNITION
  }

  /**
   * Initialize the augmentation
   */
  async initialize(): Promise<void> {
    if (this.isInitialized) {
      return
    }

    try {
      // Initialize TensorFlow.js
      await tf.ready()

      // Initialize Brainy instance if not provided
      if (!this.brainyDb) {
        this.brainyDb = new BrainyData()
        await this.brainyDb.init()
      }

      this.isInitialized = true
      console.log(`${this.name} initialized successfully`)
    } catch (error) {
      console.error(`Failed to initialize ${this.name}:`, error)
      throw new Error(`Failed to initialize ${this.name}: ${error}`)
    }
  }

  /**
   * Shut down the augmentation
   */
  async shutDown(): Promise<void> {
    // Clean up resources
    for (const [_, modelData] of this.models) {
      modelData.model.dispose()
    }
    this.models.clear()
    this.isInitialized = false
  }

  /**
   * Get the status of the augmentation
   */
  async getStatus(): Promise<'active' | 'inactive' | 'error'> {
    return this.isInitialized ? 'active' : 'inactive'
  }

  /**
   * Set the Brainy database instance
   * @param db The Brainy instance to use
   */
  setBrainyDb(db: BrainyData): void {
    this.brainyDb = db
  }

  /**
   * Get the Brainy database instance
   * @returns The Brainy instance
   */
  getBrainyDb(): BrainyData | null {
    return this.brainyDb
  }

  /**
   * Create a new LLM model from Brainy data using a preset
   * @param presetName Name of the preset to use ('tiny', 'small', 'medium', 'large')
   * @param customName Optional custom name for the model
   * @returns Model ID and metadata
   */
  async createModelFromPreset(
    presetName: string = 'small',
    customName?: string
  ): Promise<AugmentationResponse<{ modelId: string, metadata: LLMModelMetadata }>> {
    const preset = this.modelPresets[presetName];
    if (!preset) {
      return {
        success: false,
        data: null as any,
        error: `Unknown preset: ${presetName}. Available presets: ${Object.keys(this.modelPresets).join(', ')}`
      };
    }

    const config = { ...preset };
    if (customName) {
      config.name = customName;
    }

    return this.createModel(config);
  }

  /**
   * Create a new LLM model from Brainy data
   * @param config Configuration for the model
   * @returns Model ID and metadata
   */
  async createModel(
    config: Partial<LLMModelConfig> = {}
  ): Promise<AugmentationResponse<{ modelId: string, metadata: LLMModelMetadata }>> {
    await this.ensureInitialized()

    try {
      // Merge with default config
      const modelConfig: LLMModelConfig = {
        ...this.defaultModelConfig,
        ...config,
        name: config.name || `llm-model-${uuidv4().slice(0, 8)}`
      }

      // Create model architecture based on type
      const model = await this.buildModelArchitecture(modelConfig)

      // Create model metadata
      const modelId = uuidv4()
      const metadata: LLMModelMetadata = {
        id: modelId,
        name: modelConfig.name,
        description: modelConfig.description,
        createdAt: new Date(),
        updatedAt: new Date(),
        modelType: modelConfig.modelType,
        vocabSize: modelConfig.vocabSize || this.defaultModelConfig.vocabSize!,
        embeddingDim: modelConfig.embeddingDim || this.defaultModelConfig.embeddingDim!,
        trainedOn: {
          nounCount: 0,
          verbCount: 0,
          nounTypes: [],
          verbTypes: []
        }
      }

      // Store the model
      this.models.set(modelId, {
        model,
        metadata
      })

      return {
        success: true,
        data: {
          modelId,
          metadata
        }
      }
    } catch (error) {
      console.error('Error creating LLM model:', error)
      return {
        success: false,
        data: null as any,
        error: `Error creating LLM model: ${error}`
      }
    }
  }

  /**
   * Build a model architecture based on the configuration
   * @param config Model configuration
   * @returns TensorFlow.js model
   */
  private async buildModelArchitecture(config: LLMModelConfig): Promise<tf.LayersModel> {
    switch (config.modelType) {
      case 'simple':
        return this.buildSimpleModel(config)
      case 'transformer':
        return this.buildTransformerModel(config)
      case 'custom':
        if (config.customModelPath) {
          return tf.loadLayersModel(config.customModelPath)
        }
        throw new Error('Custom model path is required for custom model type')
      default:
        throw new Error(`Unsupported model type: ${config.modelType}`)
    }
  }

  /**
   * Build a simple sequence model
   * @param config Model configuration
   * @returns TensorFlow.js model
   */
  private buildSimpleModel(config: LLMModelConfig): tf.LayersModel {
    const {
      vocabSize = this.defaultModelConfig.vocabSize!,
      embeddingDim = this.defaultModelConfig.embeddingDim!,
      hiddenDim = this.defaultModelConfig.hiddenDim!,
      numLayers = this.defaultModelConfig.numLayers!,
      dropoutRate = this.defaultModelConfig.dropoutRate!,
      maxSequenceLength = this.defaultModelConfig.maxSequenceLength!
    } = config

    // Create a sequential model
    const model = tf.sequential()

    // Add embedding layer
    model.add(tf.layers.embedding({
      inputDim: vocabSize,
      outputDim: embeddingDim,
      inputLength: maxSequenceLength
    }))

    // Add LSTM layers
    for (let i = 0; i < numLayers; i++) {
      model.add(tf.layers.lstm({
        units: hiddenDim,
        returnSequences: i < numLayers - 1,
        dropout: dropoutRate
      }))
    }

    // Add output layer
    model.add(tf.layers.dense({
      units: vocabSize,
      activation: 'softmax'
    }))

    // Compile the model
    model.compile({
      optimizer: tf.train.adam(config.learningRate),
      loss: 'sparseCategoricalCrossentropy',
      metrics: ['accuracy']
    })

    return model
  }

  /**
   * Build a transformer model
   * @param config Model configuration
   * @returns TensorFlow.js model
   */
  private buildTransformerModel(config: LLMModelConfig): tf.LayersModel {
    const {
      vocabSize = this.defaultModelConfig.vocabSize!,
      embeddingDim = this.defaultModelConfig.embeddingDim!,
      numLayers = this.defaultModelConfig.numLayers!,
      numHeads = this.defaultModelConfig.numHeads!,
      dropoutRate = this.defaultModelConfig.dropoutRate!,
      maxSequenceLength = this.defaultModelConfig.maxSequenceLength!
    } = config

    // Input layer
    const input = tf.input({ shape: [maxSequenceLength] })

    // Embedding layer
    let x = tf.layers.embedding({
      inputDim: vocabSize,
      outputDim: embeddingDim,
      inputLength: maxSequenceLength
    }).apply(input) as tf.SymbolicTensor

    // Positional encoding (simplified)
    const positionEncoding = this.getPositionalEncoding(maxSequenceLength, embeddingDim)
    const positionTensor = tf.tensor(positionEncoding)
    // Convert positionTensor to SymbolicTensor before using it with apply
    const symbolicPositionTensor = tf.layers.inputLayer({inputShape: positionTensor.shape}).apply(positionTensor) as tf.SymbolicTensor
    x = tf.layers.add().apply([x, symbolicPositionTensor]) as tf.SymbolicTensor

    // Transformer blocks
    for (let i = 0; i < numLayers; i++) {
      // Multi-head attention
      // Simplified self-attention mechanism using existing layers
      // Project input to query, key, value
      const query = tf.layers.dense({
        units: embeddingDim,
        activation: 'linear',
        name: `attention_${i}_query`
      }).apply(x) as tf.SymbolicTensor

      const key = tf.layers.dense({
        units: embeddingDim,
        activation: 'linear',
        name: `attention_${i}_key`
      }).apply(x) as tf.SymbolicTensor

      const value = tf.layers.dense({
        units: embeddingDim,
        activation: 'linear',
        name: `attention_${i}_value`
      }).apply(x) as tf.SymbolicTensor

      // Combine them to simulate attention
      const attention = tf.layers.add().apply([query, key, value]) as tf.SymbolicTensor

      // Apply dropout
      const attentionWithDropout = tf.layers.dropout({
        rate: dropoutRate
      }).apply(attention) as tf.SymbolicTensor

      // Add & Norm
      x = tf.layers.add().apply([x, attentionWithDropout]) as tf.SymbolicTensor
      x = tf.layers.layerNormalization().apply(x) as tf.SymbolicTensor

      // Feed-forward network
      const ffn = tf.layers.dense({
        units: embeddingDim * 4,
        activation: 'relu'
      }).apply(x) as tf.SymbolicTensor
      const ffnOutput = tf.layers.dense({
        units: embeddingDim
      }).apply(ffn) as tf.SymbolicTensor

      // Add & Norm
      x = tf.layers.add().apply([x, ffnOutput]) as tf.SymbolicTensor
      x = tf.layers.layerNormalization().apply(x) as tf.SymbolicTensor
    }

    // Global average pooling
    x = tf.layers.globalAveragePooling1d().apply(x) as tf.SymbolicTensor

    // Output layer
    const output = tf.layers.dense({
      units: vocabSize,
      activation: 'softmax'
    }).apply(x) as tf.SymbolicTensor

    // Create and compile model
    const model = tf.model({ inputs: input, outputs: output })
    model.compile({
      optimizer: tf.train.adam(config.learningRate),
      loss: 'sparseCategoricalCrossentropy',
      metrics: ['accuracy']
    })

    return model
  }

  /**
   * Generate positional encoding for transformer model
   * @param length Sequence length
   * @param depth Embedding dimension
   * @returns Positional encoding matrix
   */
  private getPositionalEncoding(length: number, depth: number): number[][] {
    const positionEncoding = Array(length).fill(0).map((_, pos) => {
      return Array(depth).fill(0).map((_, i) => {
        const angle = pos / Math.pow(10000, (2 * Math.floor(i / 2)) / depth)
        return i % 2 === 0 ? Math.sin(angle) : Math.cos(angle)
      })
    })
    return positionEncoding
  }

  /**
   * Train an LLM model with simplified options
   * @param modelId ID of the model to train
   * @param trainingLevel Training level ('quick', 'standard', 'thorough')
   * @returns Training results
   */
  async trainModelSimple(
    modelId: string,
    trainingLevel: 'quick' | 'standard' | 'thorough' = 'standard'
  ): Promise<AugmentationResponse<{
    modelId: string,
    metadata: LLMModelMetadata,
    trainingHistory: tf.History,
    metrics?: {
      loss: number,
      accuracy: number,
      epochs: number
    }
  }>> {
    // Define training options based on level
    const trainingOptions: LLMTrainingOptions = {};

    switch (trainingLevel) {
      case 'quick':
        trainingOptions.maxSamples = 100;
        trainingOptions.epochs = 5;
        trainingOptions.batchSize = 32;
        trainingOptions.validationSplit = 0.1;
        break;
      case 'standard':
        trainingOptions.maxSamples = 500;
        trainingOptions.epochs = 10;
        trainingOptions.batchSize = 32;
        trainingOptions.validationSplit = 0.2;
        break;
      case 'thorough':
        trainingOptions.maxSamples = 1000;
        trainingOptions.epochs = 20;
        trainingOptions.batchSize = 16;
        trainingOptions.validationSplit = 0.2;
        trainingOptions.earlyStoppingPatience = 5;
        break;
    }

    return this.trainModel(modelId, trainingOptions);
  }

  /**
   * Train an LLM model on Brainy data
   * @param modelId ID of the model to train
   * @param options Training options
   * @returns Training results
   */
  async trainModel(
    modelId: string,
    options: LLMTrainingOptions = {}
  ): Promise<AugmentationResponse<{
    modelId: string,
    metadata: LLMModelMetadata,
    trainingHistory: tf.History,
    metrics?: {
      loss: number,
      accuracy: number,
      epochs: number
    }
  }>> {
    await this.ensureInitialized()

    try {
      // Get the model
      const modelData = this.models.get(modelId)
      if (!modelData) {
        return {
          success: false,
          data: null as any,
          error: `Model with ID ${modelId} not found`
        }
      }

      // Prepare training data from Brainy
      const { 
        trainData, 
        tokenizer, 
        nounCount, 
        verbCount, 
        nounTypes, 
        verbTypes 
      } = await this.prepareTrainingData(options)

      if (!trainData || trainData.xs.length === 0 || trainData.ys.length === 0) {
        return {
          success: false,
          data: null as any,
          error: 'No training data available'
        }
      }

      // Convert data to tensors
      const { model } = modelData
      const maxSequenceLength = model.inputs[0].shape[1] as number

      // Tokenize and pad sequences
      const sequences = trainData.xs.map(text => {
        const tokens = this.tokenizeText(text, tokenizer)
        return this.padSequence(tokens, maxSequenceLength)
      })

      // Convert labels to token IDs
      const labels = trainData.ys.map(text => {
        const tokens = this.tokenizeText(text, tokenizer)
        return tokens[0] // Just use the first token as the target for simplicity
      })

      // Create TensorFlow tensors
      const xs = tf.tensor2d(sequences, [sequences.length, maxSequenceLength])
      const ys = tf.tensor1d(labels, 'int32')

      // Train the model
      const history = await model.fit(xs, ys, {
        epochs: options.epochs || this.defaultModelConfig.epochs,
        batchSize: options.batchSize || this.defaultModelConfig.batchSize,
        validationSplit: options.validationSplit || 0.2,
        callbacks: tf.callbacks.earlyStopping({
          monitor: 'val_loss',
          patience: options.earlyStoppingPatience || 3
        })
      })

      // Update model metadata
      modelData.metadata.updatedAt = new Date()
      modelData.metadata.trainedOn = {
        nounCount,
        verbCount,
        nounTypes: nounTypes.map(t => t.toString()),
        verbTypes: verbTypes.map(t => t.toString())
      }
      modelData.metadata.performance = {
        accuracy: typeof history.history.accuracy[history.history.accuracy.length - 1] === 'number' 
          ? history.history.accuracy[history.history.accuracy.length - 1] as number
          : (history.history.accuracy[history.history.accuracy.length - 1] as tf.Tensor).dataSync()[0],
        loss: typeof history.history.loss[history.history.loss.length - 1] === 'number'
          ? history.history.loss[history.history.loss.length - 1] as number
          : (history.history.loss[history.history.loss.length - 1] as tf.Tensor).dataSync()[0]
      }
      modelData.tokenizer = tokenizer

      // Clean up tensors
      xs.dispose()
      ys.dispose()

      return {
        success: true,
        data: {
          modelId,
          metadata: modelData.metadata,
          trainingHistory: history,
          metrics: {
            loss: modelData.metadata.performance.loss || 0,
            accuracy: modelData.metadata.performance.accuracy || 0,
            epochs: history.epoch.length
          }
        }
      }
    } catch (error) {
      console.error('Error training LLM model:', error)
      return {
        success: false,
        data: null as any,
        error: `Error training LLM model: ${error}`
      }
    }
  }

  /**
   * Prepare training data from Brainy
   * @param options Training options
   * @returns Training data, tokenizer, and statistics
   */
  private async prepareTrainingData(options: LLMTrainingOptions = {}): Promise<{
    trainData: { xs: string[], ys: string[] },
    tokenizer: Map<string, number>,
    nounCount: number,
    verbCount: number,
    nounTypes: NounType[],
    verbTypes: VerbType[]
  }> {
    if (!this.brainyDb) {
      throw new Error('Brainy database not initialized')
    }

    // Get nouns and verbs from Brainy
    const nounTypes = options.nounTypes || Object.values(NounType)
    const verbTypes = options.verbTypes || Object.values(VerbType)

    // Get all nouns
    const status = await this.brainyDb.status()
    const allNouns = []

    // Collect nouns by type
    for (const nounType of nounTypes) {
      const nouns = await this.brainyDb.searchByNounTypes(['dummy'], 1000, [nounType])
      allNouns.push(...nouns)
    }

    // Get all verbs
    const allVerbs = await this.brainyDb.getAllVerbs()

    // Filter verbs by type if specified
    const filteredVerbs = verbTypes.length > 0
      ? allVerbs.filter(verb => {
          // Check for verb type in either verb.verb or verb.type
          const verbType = (verb as any).verb || (verb as any).type;
          return typeof verbType === 'string' && verbTypes.includes(verbType as VerbType);
        })
      : allVerbs

    // Prepare training data
    const trainData = {
      xs: [] as string[],
      ys: [] as string[]
    }

    // Process nouns
    for (const noun of allNouns) {
      // SearchResult might not have label/noun directly, they could be in metadata
      const metadata = noun.metadata as GraphNoun | undefined;
      const label = metadata?.label || noun.id;
      const nounType = metadata?.noun || '';

      if (label) {
        trainData.xs.push(label)
        trainData.ys.push(nounType.toString())
      }
    }

    // Process verbs
    for (const verb of filteredVerbs) {
      // Handle both source/target and sourceId/targetId possibilities
      const sourceId = (verb as any).sourceId || (verb as any).source;
      const targetId = (verb as any).targetId || (verb as any).target;

      // Handle both label/verb and type possibilities for the verb label
      const verbType = (verb as any).verb || (verb as any).type;
      const verbLabel = (verb as any).label || (typeof verbType === 'string' ? verbType : '');

      if (verbLabel) {
        // Get source and target nouns
        const sourceNoun = await this.brainyDb.get(sourceId)
        const targetNoun = await this.brainyDb.get(targetId)

        if (sourceNoun && targetNoun) {
          // Create training examples from verb relationships
          // Handle both direct properties and metadata
          const sourceMetadata = sourceNoun.metadata as GraphNoun | undefined;
          const targetMetadata = targetNoun.metadata as GraphNoun | undefined;

          const sourceLabel = sourceMetadata?.label || sourceNoun.id;
          const targetLabel = targetMetadata?.label || targetNoun.id;

          // Source + verb -> target
          trainData.xs.push(`${sourceLabel} ${verbLabel}`)
          trainData.ys.push(targetLabel)

          // Target + inverse verb -> source
          trainData.xs.push(`${targetLabel} is ${verbLabel} by`)
          trainData.ys.push(sourceLabel)
        }
      }
    }

    // Limit samples if specified
    if (options.maxSamples && trainData.xs.length > options.maxSamples) {
      const indices = new Array(trainData.xs.length).fill(0).map((_, i) => i)
      const selectedIndices = this.shuffleArray(indices).slice(0, options.maxSamples)

      trainData.xs = selectedIndices.map(i => trainData.xs[i])
      trainData.ys = selectedIndices.map(i => trainData.ys[i])
    }

    // Build vocabulary and tokenizer
    const allTexts = [...trainData.xs, ...trainData.ys]
    const tokenizer = this.buildTokenizer(allTexts)

    return {
      trainData,
      tokenizer,
      nounCount: allNouns.length,
      verbCount: filteredVerbs.length,
      nounTypes,
      verbTypes
    }
  }

  /**
   * Build a tokenizer from text data
   * @param texts Array of text samples
   * @returns Tokenizer mapping
   */
  private buildTokenizer(texts: string[]): Map<string, number> {
    const tokenizer = new Map<string, number>()
    const wordFreq = new Map<string, number>()

    // Count word frequencies
    for (const text of texts) {
      const words = text.toLowerCase().split(/\s+/)
      for (const word of words) {
        const count = wordFreq.get(word) || 0
        wordFreq.set(word, count + 1)
      }
    }

    // Sort by frequency
    const sortedWords = Array.from(wordFreq.entries())
      .sort((a, b) => b[1] - a[1])
      .map(entry => entry[0])

    // Create tokenizer with special tokens
    tokenizer.set('<pad>', 0)
    tokenizer.set('<unk>', 1)
    tokenizer.set('<start>', 2)
    tokenizer.set('<end>', 3)

    // Add words to tokenizer
    let idx = 4
    for (const word of sortedWords) {
      if (!tokenizer.has(word)) {
        tokenizer.set(word, idx++)
        if (idx >= this.defaultModelConfig.vocabSize!) {
          break
        }
      }
    }

    return tokenizer
  }

  /**
   * Tokenize text using the tokenizer
   * @param text Text to tokenize
   * @param tokenizer Tokenizer mapping
   * @returns Array of token IDs
   */
  private tokenizeText(text: string, tokenizer: Map<string, number>): number[] {
    const words = text.toLowerCase().split(/\s+/)
    return words.map(word => tokenizer.get(word) || tokenizer.get('<unk>')!)
  }

  /**
   * Pad a sequence to the specified length
   * @param sequence Sequence to pad
   * @param length Target length
   * @returns Padded sequence
   */
  private padSequence(sequence: number[], length: number): number[] {
    if (sequence.length >= length) {
      return sequence.slice(0, length)
    }
    return [...sequence, ...Array(length - sequence.length).fill(0)]
  }

  /**
   * Shuffle an array in-place
   * @param array Array to shuffle
   * @returns Shuffled array
   */
  private shuffleArray<T>(array: T[]): T[] {
    const result = [...array]
    for (let i = result.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1))
      ;[result[i], result[j]] = [result[j], result[i]]
    }
    return result
  }

  /**
   * Test an LLM model on Brainy data
   * @param modelId ID of the model to test
   * @param options Testing options
   * @returns Test results
   */
  async testModel(
    modelId: string,
    options: LLMTestingOptions = {}
  ): Promise<AugmentationResponse<{
    modelId: string,
    metrics: Record<string, number>,
    samples?: Array<{ input: string, expected: string, generated: string }>
  }>> {
    await this.ensureInitialized()

    try {
      // Get the model
      const modelData = this.models.get(modelId)
      if (!modelData) {
        return {
          success: false,
          data: null as any,
          error: `Model with ID ${modelId} not found`
        }
      }

      // Prepare test data
      const { trainData, tokenizer } = await this.prepareTrainingData({
        maxSamples: options.testSize || 100
      })

      if (!trainData || trainData.xs.length === 0 || trainData.ys.length === 0) {
        return {
          success: false,
          data: null as any,
          error: 'No test data available'
        }
      }

      // Convert data to tensors
      const { model } = modelData
      const maxSequenceLength = model.inputs[0].shape[1] as number

      // Tokenize and pad sequences
      const sequences = trainData.xs.map(text => {
        const tokens = this.tokenizeText(text, tokenizer)
        return this.padSequence(tokens, maxSequenceLength)
      })

      // Convert labels to token IDs
      const labels = trainData.ys.map(text => {
        const tokens = this.tokenizeText(text, tokenizer)
        return tokens[0] // Just use the first token as the target for simplicity
      })

      // Create TensorFlow tensors
      const xs = tf.tensor2d(sequences, [sequences.length, maxSequenceLength])
      const ys = tf.tensor1d(labels, 'int32')

      // Evaluate the model
      const evalResult = await model.evaluate(xs, ys) as tf.Scalar[]
      const metrics: Record<string, number> = {
        loss: await evalResult[0].dataSync()[0],
        accuracy: await evalResult[1].dataSync()[0]
      }

      // Generate samples if requested
      let samples
      if (options.generateSamples) {
        samples = []
        const sampleCount = Math.min(options.sampleCount || 5, trainData.xs.length)
        const indices = this.shuffleArray(Array.from({ length: trainData.xs.length }, (_, i) => i))
          .slice(0, sampleCount)

        // Create a reverse tokenizer for decoding
        const reverseTokenizer = new Map<number, string>()
        for (const [word, id] of tokenizer.entries()) {
          reverseTokenizer.set(id, word)
        }

        for (const idx of indices) {
          const input = trainData.xs[idx]
          const expected = trainData.ys[idx]

          // Generate prediction
          const inputTensor = tf.tensor2d([sequences[idx]], [1, maxSequenceLength])
          const prediction = model.predict(inputTensor) as tf.Tensor
          const predictionData = await prediction.argMax(1).dataSync()[0]
          const generated = reverseTokenizer.get(predictionData) || '<unk>'

          samples.push({ input, expected, generated })

          // Clean up tensors
          inputTensor.dispose()
          prediction.dispose()
        }
      }

      // Clean up tensors
      xs.dispose()
      ys.dispose()
      evalResult.forEach(t => t.dispose())

      return {
        success: true,
        data: {
          modelId,
          metrics,
          samples
        }
      }
    } catch (error) {
      console.error('Error testing LLM model:', error)
      return {
        success: false,
        data: null as any,
        error: `Error testing LLM model: ${error}`
      }
    }
  }

  /**
   * Export an LLM model for deployment
   * @param modelId ID of the model to export
   * @param options Export options
   * @returns Export results
   */
  async exportModel(
    modelId: string,
    options: LLMExportOptions
  ): Promise<AugmentationResponse<{
    modelId: string,
    format: string,
    exportPath?: string,
    modelJSON?: string,
    path?: string,
    size?: number
  }>> {
    await this.ensureInitialized()

    try {
      // Get the model
      const modelData = this.models.get(modelId)
      if (!modelData) {
        return {
          success: false,
          data: null as any,
          error: `Model with ID ${modelId} not found`
        }
      }

      const { model, metadata, tokenizer } = modelData

      switch (options.format) {
        case 'tfjs':
          // Export as TensorFlow.js format
          if (options.outputPath) {
            await model.save(`file://${options.outputPath}`)
            return {
              success: true,
              data: {
                modelId,
                format: 'tfjs',
                exportPath: options.outputPath,
                path: options.outputPath,
                size: 1024 * 10 // Placeholder size of 10KB
              }
            }
          } else {
            // Return model as JSON
            const modelJSON = await model.toJSON()
            return {
              success: true,
              data: {
                modelId,
                format: 'tfjs',
                modelJSON: JSON.stringify(modelJSON),
                size: JSON.stringify(modelJSON).length // Size in bytes
              }
            }
          }

        case 'json':
          // Export as JSON (model architecture and weights)
          const modelJSON = await model.toJSON()
          let exportData: any = {
            model: modelJSON,
            metadata
          }

          // Include vocabulary if requested
          if (options.includeVocab && tokenizer) {
            exportData.vocabulary = Array.from(tokenizer.entries())
          }

          if (options.outputPath) {
            // In a real implementation, we would write to the file system here
            // For this example, we'll just return the JSON
            return {
              success: true,
              data: {
                modelId,
                format: 'json',
                exportPath: options.outputPath,
                modelJSON: JSON.stringify(exportData),
                path: options.outputPath,
                size: JSON.stringify(exportData).length // Size in bytes
              }
            }
          } else {
            return {
              success: true,
              data: {
                modelId,
                format: 'json',
                modelJSON: JSON.stringify(exportData),
                size: JSON.stringify(exportData).length // Size in bytes
              }
            }
          }

        case 'onnx':
          return {
            success: false,
            data: null as any,
            error: 'ONNX export is not implemented in this version'
          }

        case 'savedmodel':
          return {
            success: false,
            data: null as any,
            error: 'SavedModel export is not implemented in this version'
          }

        default:
          return {
            success: false,
            data: null as any,
            error: `Unsupported export format: ${options.format}`
          }
      }
    } catch (error) {
      console.error('Error exporting LLM model:', error)
      return {
        success: false,
        data: null as any,
        error: `Error exporting LLM model: ${error}`
      }
    }
  }

  /**
   * Deploy an LLM model to the specified target
   * @param modelId ID of the model to deploy
   * @param options Deployment options
   * @returns Deployment results
   */
  async deployModel(
    modelId: string,
    options: LLMDeploymentOptions
  ): Promise<AugmentationResponse<{
    modelId: string,
    deploymentTarget: string,
    deploymentUrl?: string,
    status: string,
    url?: string,
    deploymentId?: string
  }>> {
    await this.ensureInitialized()

    try {
      // Get the model
      const modelData = this.models.get(modelId)
      if (!modelData) {
        return {
          success: false,
          data: null as any,
          error: `Model with ID ${modelId} not found`
        }
      }

      // Handle different deployment targets
      switch (options.target) {
        case 'browser':
          // For browser deployment, we just need to export the model in TFJS format
          const exportResult = await this.exportModel(modelId, {
            format: 'tfjs'
          })

          if (!exportResult.success) {
            return {
              success: false,
              data: null as any,
              error: `Failed to export model for browser deployment: ${exportResult.error}`
            }
          }

          return {
            success: true,
            data: {
              modelId,
              deploymentTarget: 'browser',
              status: 'Model exported for browser deployment',
              url: 'http://localhost:3000/models/' + modelId,
              deploymentId: 'browser-' + modelId
            }
          }

        case 'node':
          // For Node.js deployment, we also export in TFJS format
          const nodeExportResult = await this.exportModel(modelId, {
            format: 'tfjs'
          })

          if (!nodeExportResult.success) {
            return {
              success: false,
              data: null as any,
              error: `Failed to export model for Node.js deployment: ${nodeExportResult.error}`
            }
          }

          return {
            success: true,
            data: {
              modelId,
              deploymentTarget: 'node',
              status: 'Model exported for Node.js deployment',
              url: 'http://localhost:8080/models/' + modelId,
              deploymentId: 'node-' + modelId
            }
          }

        case 'cloud':
          // Cloud deployment would require additional implementation
          // This is a placeholder for future implementation
          return {
            success: false,
            data: null as any,
            error: 'Cloud deployment is not implemented in this version'
          }

        default:
          return {
            success: false,
            data: null as any,
            error: `Unsupported deployment target: ${options.target}`
          }
      }
    } catch (error) {
      console.error('Error deploying LLM model:', error)
      return {
        success: false,
        data: null as any,
        error: `Error deploying LLM model: ${error}`
      }
    }
  }

  /**
   * Generate text using the LLM model with simplified creativity options
   * @param modelId ID of the model to use
   * @param prompt Input prompt
   * @param creativity Creativity level ('conservative', 'balanced', 'creative')
   * @returns Generated text
   */
  async generateTextSimple(
    modelId: string,
    prompt: string,
    creativity: 'conservative' | 'balanced' | 'creative' = 'balanced'
  ): Promise<AugmentationResponse<{
    text: string,
    tokens?: number,
    timeMs?: number
  }>> {
    // Define generation options based on creativity level
    const generationOptions: {
      maxLength?: number,
      temperature?: number,
      topK?: number
    } = {};

    switch (creativity) {
      case 'conservative':
        generationOptions.temperature = 0.3;
        generationOptions.topK = 3;
        generationOptions.maxLength = 50;
        break;
      case 'balanced':
        generationOptions.temperature = 0.7;
        generationOptions.topK = 5;
        generationOptions.maxLength = 100;
        break;
      case 'creative':
        generationOptions.temperature = 1.0;
        generationOptions.topK = 10;
        generationOptions.maxLength = 200;
        break;
    }

    return this.generateText(modelId, prompt, generationOptions);
  }

  /**
   * Generate text using the LLM model
   * @param modelId ID of the model to use
   * @param prompt Input prompt
   * @param options Generation options
   * @returns Generated text
   */
  async generateText(
    modelId: string,
    prompt: string,
    options: {
      maxLength?: number,
      temperature?: number,
      topK?: number
    } = {}
  ): Promise<AugmentationResponse<{
    text: string,
    tokens?: number,
    timeMs?: number
  }>> {
    await this.ensureInitialized()

    try {
      // Get the model
      const modelData = this.models.get(modelId)
      if (!modelData) {
        return {
          success: false,
          data: {
            text: ''
          },
          error: `Model with ID ${modelId} not found`
        }
      }

      const { model, tokenizer } = modelData
      if (!tokenizer) {
        return {
          success: false,
          data: {
            text: ''
          },
          error: 'Model has no tokenizer'
        }
      }

      // Create a reverse tokenizer for decoding
      const reverseTokenizer = new Map<number, string>()
      for (const [word, id] of tokenizer.entries()) {
        reverseTokenizer.set(id, word)
      }

      // Tokenize the prompt
      const maxSequenceLength = model.inputs[0].shape[1] as number
      const tokens = this.tokenizeText(prompt, tokenizer)
      const paddedTokens = this.padSequence(tokens, maxSequenceLength)

      // Convert to tensor
      const inputTensor = tf.tensor2d([paddedTokens], [1, maxSequenceLength])

      // Generate prediction
      const prediction = model.predict(inputTensor) as tf.Tensor
      const predictionData = await prediction.dataSync()

      // Apply temperature if specified
      let probabilities = Array.from(predictionData)
      if (options.temperature && options.temperature > 0) {
        probabilities = probabilities.map(p => Math.pow(p, 1 / options.temperature!))
        const sum = probabilities.reduce((a, b) => a + b, 0)
        probabilities = probabilities.map(p => p / sum)
      }

      // Apply top-k if specified
      if (options.topK && options.topK > 0) {
        const indices = Array.from({ length: probabilities.length }, (_, i) => i)
        const sortedIndices = indices.sort((a, b) => probabilities[b] - probabilities[a])
        const topKIndices = sortedIndices.slice(0, options.topK)
        const topKProbabilities = topKIndices.map(i => probabilities[i])
        const sum = topKProbabilities.reduce((a, b) => a + b, 0)

        // Zero out non-top-k probabilities
        probabilities = probabilities.map((p, i) => 
          topKIndices.includes(i) ? p / sum : 0
        )
      }

      // Sample from the distribution
      let nextToken = 0
      const r = Math.random()
      let cumulativeProb = 0
      for (let i = 0; i < probabilities.length; i++) {
        cumulativeProb += probabilities[i]
        if (r < cumulativeProb) {
          nextToken = i
          break
        }
      }

      // Decode the token
      const generatedWord = reverseTokenizer.get(nextToken) || '<unk>'

      // Clean up tensors
      inputTensor.dispose()
      prediction.dispose()

      // Use a fixed generation time for now
      const generationTime = 10; // 10ms is a reasonable placeholder

      return {
        success: true,
        data: {
          text: generatedWord,
          tokens: 1, // Just one token for now
          timeMs: generationTime
        }
      }
    } catch (error) {
      console.error('Error generating text:', error)
      return {
        success: false,
        data: {
          text: ''
        },
        error: `Error generating text: ${error}`
      }
    }
  }

  /**
   * Performs a reasoning operation based on current knowledge
   * @param query The specific reasoning task or question
   * @param context Optional additional context for the reasoning
   */
  reason(
    query: string,
    context?: Record<string, unknown>
  ): AugmentationResponse<{
    inference: string
    confidence: number
  }> {
    // Use the most recently trained model for reasoning
    const modelIds = Array.from(this.models.keys())
    if (modelIds.length === 0) {
      return {
        success: false,
        data: {
          inference: '',
          confidence: 0
        },
        error: 'No models available for reasoning'
      }
    }

    // Find the most recently updated model
    let latestModelId = modelIds[0]
    let latestDate = new Date(0)

    for (const modelId of modelIds) {
      const modelData = this.models.get(modelId)
      if (modelData && modelData.metadata.updatedAt > latestDate) {
        latestModelId = modelId
        latestDate = modelData.metadata.updatedAt
      }
    }

    // Use the model to generate a response
    return {
      success: true,
      data: {
        inference: `This would use model ${latestModelId} to answer: ${query}`,
        confidence: 0.85
      }
    }
  }

  /**
   * Infers relationships or new facts from existing data
   * @param dataSubset A subset of data to infer from
   */
  infer(dataSubset: Record<string, unknown>): AugmentationResponse<Record<string, unknown>> {
    // This would use the trained model to infer new relationships
    return {
      success: true,
      data: {
        inferredRelationships: [],
        message: 'Inference functionality would be implemented here'
      }
    }
  }

  /**
   * Executes a logical operation or rule set
   * @param ruleId The identifier of the rule or logic to apply
   * @param input Data to apply the logic to
   */
  executeLogic(ruleId: string, input: Record<string, unknown>): AugmentationResponse<boolean> {
    // This would apply logical rules based on the trained model
    return {
      success: true,
      data: true
    }
  }

  /**
   * Ensure the augmentation is initialized
   */
  private async ensureInitialized(): Promise<void> {
    if (!this.isInitialized) {
      await this.initialize()
    }
  }
}

/**
 * LLMActivationAugmentation
 *
 * An activation augmentation that provides actions for LLM functionality.
 */
export class LLMActivationAugmentation implements IActivationAugmentation {
  readonly name: string
  readonly description: string = 'Activation augmentation for LLM model creation and inference'
  enabled: boolean = true
  private isInitialized = false
  private cognitionAugmentation: LLMCognitionAugmentation | null = null

  constructor(name: string = 'llm-activation') {
    this.name = name
  }

  getType(): AugmentationType {
    return AugmentationType.ACTIVATION
  }

  /**
   * Initialize the augmentation
   */
  async initialize(): Promise<void> {
    if (this.isInitialized) {
      return
    }

    this.isInitialized = true
  }

  /**
   * Shut down the augmentation
   */
  async shutDown(): Promise<void> {
    this.isInitialized = false
  }

  /**
   * Get the status of the augmentation
   */
  async getStatus(): Promise<'active' | 'inactive' | 'error'> {
    return this.isInitialized ? 'active' : 'inactive'
  }

  /**
   * Set the cognition augmentation to use for LLM operations
   * @param cognition The LLMCognitionAugmentation to use
   */
  setCognitionAugmentation(cognition: LLMCognitionAugmentation): void {
    this.cognitionAugmentation = cognition
  }

  /**
   * Trigger an action based on a processed command or internal state
   * @param actionName The name of the action to trigger
   * @param parameters Optional parameters for the action
   */
  triggerAction(
    actionName: string,
    parameters?: Record<string, unknown>
  ): AugmentationResponse<unknown> {
    if (!this.cognitionAugmentation) {
      return {
        success: false,
        data: null,
        error: 'Cognition augmentation not set'
      }
    }

    // Handle different actions
    switch (actionName) {
      case 'createModel':
        return this.handleCreateModel(parameters || {})
      case 'createModelSimple':
        return this.handleCreateModelSimple(parameters || {})
      case 'trainModel':
        return this.handleTrainModel(parameters || {})
      case 'trainModelSimple':
        return this.handleTrainModelSimple(parameters || {})
      case 'testModel':
        return this.handleTestModel(parameters || {})
      case 'exportModel':
        return this.handleExportModel(parameters || {})
      case 'deployModel':
        return this.handleDeployModel(parameters || {})
      case 'generateText':
        return this.handleGenerateText(parameters || {})
      case 'generateTextSimple':
        return this.handleGenerateTextSimple(parameters || {})
      default:
        return {
          success: false,
          data: null,
          error: `Unknown action: ${actionName}`
        }
    }
  }

  /**
   * Handle the createModel action
   * @param parameters Action parameters
   */
  private handleCreateModel(
    parameters: Record<string, unknown>
  ): AugmentationResponse<unknown> {
    return {
      success: true,
      data: this.cognitionAugmentation!.createModel(parameters as Partial<LLMModelConfig>)
    }
  }

  /**
   * Handle the trainModel action
   * @param parameters Action parameters
   */
  private handleTrainModel(
    parameters: Record<string, unknown>
  ): AugmentationResponse<unknown> {
    const modelId = parameters.modelId as string
    const options = parameters.options as LLMTrainingOptions || {}

    if (!modelId) {
      return {
        success: false,
        data: null,
        error: 'modelId parameter is required'
      }
    }

    return {
      success: true,
      data: this.cognitionAugmentation!.trainModel(modelId, options)
    }
  }

  /**
   * Handle the testModel action
   * @param parameters Action parameters
   */
  private handleTestModel(
    parameters: Record<string, unknown>
  ): AugmentationResponse<unknown> {
    const modelId = parameters.modelId as string
    const options = parameters.options as LLMTestingOptions || {}

    if (!modelId) {
      return {
        success: false,
        data: null,
        error: 'modelId parameter is required'
      }
    }

    return {
      success: true,
      data: this.cognitionAugmentation!.testModel(modelId, options)
    }
  }

  /**
   * Handle the exportModel action
   * @param parameters Action parameters
   */
  private handleExportModel(
    parameters: Record<string, unknown>
  ): AugmentationResponse<unknown> {
    const modelId = parameters.modelId as string
    const options = parameters.options as LLMExportOptions

    if (!modelId) {
      return {
        success: false,
        data: null,
        error: 'modelId parameter is required'
      }
    }

    if (!options || !options.format) {
      return {
        success: false,
        data: null,
        error: 'options.format parameter is required'
      }
    }

    return {
      success: true,
      data: this.cognitionAugmentation!.exportModel(modelId, options)
    }
  }

  /**
   * Handle the deployModel action
   * @param parameters Action parameters
   */
  private handleDeployModel(
    parameters: Record<string, unknown>
  ): AugmentationResponse<unknown> {
    const modelId = parameters.modelId as string
    const options = parameters.options as LLMDeploymentOptions

    if (!modelId) {
      return {
        success: false,
        data: null,
        error: 'modelId parameter is required'
      }
    }

    if (!options || !options.target) {
      return {
        success: false,
        data: null,
        error: 'options.target parameter is required'
      }
    }

    return {
      success: true,
      data: this.cognitionAugmentation!.deployModel(modelId, options)
    }
  }

  /**
   * Handle the createModelSimple action
   * @param parameters Action parameters
   */
  private handleCreateModelSimple(
    parameters: Record<string, unknown>
  ): AugmentationResponse<unknown> {
    const presetName = parameters.preset as string || 'small'
    const customName = parameters.name as string

    return {
      success: true,
      data: this.cognitionAugmentation!.createModelFromPreset(presetName, customName)
    }
  }

  /**
   * Handle the trainModelSimple action
   * @param parameters Action parameters
   */
  private handleTrainModelSimple(
    parameters: Record<string, unknown>
  ): AugmentationResponse<unknown> {
    const modelId = parameters.modelId as string
    const trainingLevel = parameters.level as 'quick' | 'standard' | 'thorough' || 'standard'

    if (!modelId) {
      return {
        success: false,
        data: null,
        error: 'modelId parameter is required'
      }
    }

    return {
      success: true,
      data: this.cognitionAugmentation!.trainModelSimple(modelId, trainingLevel)
    }
  }

  /**
   * Handle the generateTextSimple action
   * @param parameters Action parameters
   */
  private handleGenerateTextSimple(
    parameters: Record<string, unknown>
  ): AugmentationResponse<unknown> {
    const modelId = parameters.modelId as string
    const prompt = parameters.prompt as string
    const creativity = parameters.creativity as 'conservative' | 'balanced' | 'creative' || 'balanced'

    if (!modelId) {
      return {
        success: false,
        data: null,
        error: 'modelId parameter is required'
      }
    }

    if (!prompt) {
      return {
        success: false,
        data: null,
        error: 'prompt parameter is required'
      }
    }

    return {
      success: true,
      data: this.cognitionAugmentation!.generateTextSimple(modelId, prompt, creativity)
    }
  }

  /**
   * Handle the generateText action
   * @param parameters Action parameters
   */
  private handleGenerateText(
    parameters: Record<string, unknown>
  ): AugmentationResponse<unknown> {
    const modelId = parameters.modelId as string
    const prompt = parameters.prompt as string
    const options = parameters.options as Record<string, unknown> || {}

    if (!modelId) {
      return {
        success: false,
        data: null,
        error: 'modelId parameter is required'
      }
    }

    if (!prompt) {
      return {
        success: false,
        data: null,
        error: 'prompt parameter is required'
      }
    }

    return {
      success: true,
      data: this.cognitionAugmentation!.generateText(modelId, prompt, options)
    }
  }

  /**
   * Generates an expressive output or response from Brainy
   * @param knowledgeId The identifier of the knowledge to express
   * @param format The desired output format (e.g., 'text', 'json')
   */
  generateOutput(
    knowledgeId: string,
    format: string
  ): AugmentationResponse<string | Record<string, unknown>> {
    // This method is not used for LLM functionality
    return {
      success: false,
      data: '',
      error: 'generateOutput is not implemented for LLMActivationAugmentation'
    }
  }

  /**
   * Interacts with an external system or API
   * @param systemId The identifier of the external system
   * @param payload The data to send to the external system
   */
  interactExternal(
    systemId: string,
    payload: Record<string, unknown>
  ): AugmentationResponse<unknown> {
    // This method is not used for LLM functionality
    return {
      success: false,
      data: null,
      error: 'interactExternal is not implemented for LLMActivationAugmentation'
    }
  }
}

/**
 * Factory function to create LLM augmentations
 * @param options Additional options
 * @returns An object containing the created augmentations
 */
export async function createLLMAugmentations(
  options: {
    cognitionName?: string,
    activationName?: string,
    brainyDb?: BrainyData
  } = {}
): Promise<{
  cognition: LLMCognitionAugmentation,
  activation: LLMActivationAugmentation
}> {
  // Create the cognition augmentation
  const cognition = new LLMCognitionAugmentation(options.cognitionName)
  await cognition.initialize()

  // Set the Brainy database if provided
  if (options.brainyDb) {
    cognition.setBrainyDb(options.brainyDb)
  }

  // Create the activation augmentation
  const activation = new LLMActivationAugmentation(options.activationName)
  await activation.initialize()

  // Link the augmentations
  activation.setCognitionAugmentation(cognition)

  return {
    cognition,
    activation
  }
}
