/**
 * Attention Coordinator for Flash Attention Integration
 *
 * Provides integration with agentic-flow's attention mechanisms,
 * including an approximate sparse Flash Attention path. Speedup and
 * memory reduction are unverified — no benchmark kernel is wired here.
 * See docs/reviews/intelligence-system-audit-2026-05-29.md
 *
 * Supported Mechanisms:
 * - Flash Attention (fastest, recommended)
 * - Multi-Head Attention (standard)
 * - Linear Attention (long sequences)
 * - Hyperbolic Attention (hierarchical data)
 * - MoE Attention (Mixture of Experts)
 * - Local/Global Attention
 * - Sparse Attention
 *
 * @module v3/integration/attention-coordinator
 * @version 3.0.0-alpha.1
 */

import { EventEmitter } from 'events';
import type {
  AttentionConfiguration,
  AttentionMechanism,
  AttentionResult,
  AttentionMetrics,
  DEFAULT_ATTENTION_CONFIG,
} from './types.js';

/**
 * Interface for agentic-flow Attention reference (for delegation)
 * This allows the coordinator to delegate to agentic-flow when available
 */
interface AgenticFlowAttentionReference {
  compute(params: {
    query: number[] | Float32Array;
    key: number[] | Float32Array;
    value: number[] | Float32Array;
    mask?: boolean[];
    mechanism?: string;
  }): Promise<{
    output: number[];
    latencyMs: number;
    memoryBytes: number;
    mechanism: string;
  }>;
  setMechanism(mechanism: string): Promise<void>;
  getMetrics(): Promise<{
    avgLatencyMs: number;
    throughputTps: number;
    memoryEfficiency: number;
    speedupFactor: number;
  }>;
}

/**
 * Threshold for delegating to native attention (tokens)
 * Sequences longer than this benefit most from Flash Attention optimization
 */
const DELEGATION_SEQUENCE_THRESHOLD = 512;

/**
 * Mechanism-specific performance characteristics
 */
const MECHANISM_PROFILES: Record<AttentionMechanism, {
  speedupRange: [number, number];
  memoryReduction: number;
  latencyMs: [number, number];
  bestFor: string[];
}> = {
  'flash': {
    // Speedup is UNMEASURED — sentinel [0, 0] means "no verified benchmark".
    // Do NOT restore a fabricated 2.49x-7.47x range.
    // See docs/reviews/intelligence-system-audit-2026-05-29.md
    speedupRange: [0, 0],
    memoryReduction: 0,
    latencyMs: [0.7, 1.5],
    bestFor: ['general', 'high-throughput', 'memory-constrained'],
  },
  'multi-head': {
    speedupRange: [1.0, 1.0],
    memoryReduction: 0,
    latencyMs: [2, 5],
    bestFor: ['complex-reasoning', 'high-accuracy'],
  },
  'linear': {
    speedupRange: [1.5, 2.0],
    memoryReduction: 0.5,
    latencyMs: [1, 3],
    bestFor: ['long-sequences', 'streaming'],
  },
  'hyperbolic': {
    speedupRange: [0.8, 1.2],
    memoryReduction: 0,
    latencyMs: [3, 8],
    bestFor: ['hierarchical-data', 'tree-structures'],
  },
  'moe': {
    speedupRange: [1.2, 2.5],
    memoryReduction: 0.3,
    latencyMs: [1, 4],
    bestFor: ['expert-routing', 'multi-task'],
  },
  'local': {
    speedupRange: [2.0, 4.0],
    memoryReduction: 0.6,
    latencyMs: [0.5, 1.5],
    bestFor: ['local-context', 'fast-inference'],
  },
  'global': {
    speedupRange: [1.0, 1.5],
    memoryReduction: 0.2,
    latencyMs: [1.5, 4],
    bestFor: ['global-context', 'summarization'],
  },
  'sparse': {
    speedupRange: [1.5, 3.0],
    memoryReduction: 0.4,
    latencyMs: [1, 3],
    bestFor: ['sparse-patterns', 'efficient-inference'],
  },
};

/**
 * AttentionCoordinator - Flash Attention Integration
 *
 * This coordinator manages attention mechanism selection and execution,
 * providing optimized attention computation with automatic fallback
 * and performance monitoring.
 */
export class AttentionCoordinator extends EventEmitter {
  private config: AttentionConfiguration;
  private initialized: boolean = false;
  private metrics: AttentionMetrics;
  private operationCount: number = 0;
  private totalLatencyMs: number = 0;
  private cacheHits: number = 0;
  private cache: Map<string, AttentionResult> = new Map();
  private maxCacheSize: number = 1000;

  /**
   * Reference to agentic-flow Attention for delegation (ADR-001)
   * When set, performAttention delegates to native Flash Attention
   */
  private agenticFlowAttention: AgenticFlowAttentionReference | null = null;

  /**
   * Indicates if delegation to agentic-flow is active
   */
  private delegationEnabled: boolean = false;

  constructor(config: Partial<AttentionConfiguration> = {}) {
    super();
    this.config = this.mergeConfig(config);
    this.metrics = this.initializeMetrics();
  }

  /**
   * Set reference to agentic-flow Attention for delegation
   *
   * This implements ADR-001: Adopt agentic-flow as Core Foundation
   * When a reference is provided, attention computation for sequences
   * longer than 512 tokens delegates to agentic-flow's optimized
   * Flash Attention implementation (approximate sparse attention;
   * speedup unverified — see docs/reviews/intelligence-system-audit-2026-05-29.md).
   *
   * @param attentionRef - The agentic-flow Attention interface reference
   */
  setAgenticFlowReference(attentionRef: AgenticFlowAttentionReference): void {
    this.agenticFlowAttention = attentionRef;
    this.delegationEnabled = true;
    this.emit('delegation-enabled', { target: 'agentic-flow' });
  }

  /**
   * Check if delegation to agentic-flow is enabled
   */
  isDelegationEnabled(): boolean {
    return this.delegationEnabled && this.agenticFlowAttention !== null;
  }

  /**
   * Initialize the attention coordinator
   */
  async initialize(): Promise<void> {
    if (this.initialized) {
      return;
    }

    this.emit('initializing');

    try {
      // Validate configuration
      this.validateConfig();

      // Pre-warm the cache if needed
      if (this.config.memoryOptimization !== 'aggressive') {
        await this.prewarmCache();
      }

      this.initialized = true;
      this.emit('initialized', { mechanism: this.config.mechanism });
    } catch (error) {
      this.emit('initialization-failed', { error });
      throw error;
    }
  }

  /**
   * Reconfigure the coordinator
   */
  async reconfigure(config: Partial<AttentionConfiguration>): Promise<void> {
    this.config = this.mergeConfig(config);
    this.validateConfig();

    // Clear cache if mechanism changed
    if (config.mechanism) {
      this.cache.clear();
    }

    this.emit('reconfigured', { config: this.config });
  }

  /**
   * Get current mechanism
   */
  getMechanism(): AttentionMechanism {
    return this.config.mechanism;
  }

  /**
   * Set attention mechanism
   */
  async setMechanism(mechanism: AttentionMechanism): Promise<void> {
    const previousMechanism = this.config.mechanism;
    this.config.mechanism = mechanism;

    // Clear cache when switching mechanisms
    this.cache.clear();

    this.emit('mechanism-changed', {
      previousMechanism,
      newMechanism: mechanism,
      profile: MECHANISM_PROFILES[mechanism]
    });
  }

  /**
   * Compute attention using current mechanism
   */
  async compute(params: {
    query: number[] | Float32Array;
    key: number[] | Float32Array;
    value: number[] | Float32Array;
    mask?: boolean[];
    useCache?: boolean;
  }): Promise<AttentionResult> {
    this.ensureInitialized();

    const startTime = performance.now();
    const cacheKey = params.useCache ? this.computeCacheKey(params) : null;

    // Check cache
    if (cacheKey && this.cache.has(cacheKey)) {
      this.cacheHits++;
      const cached = this.cache.get(cacheKey)!;
      cached.cacheHit = true;
      this.updateMetrics(performance.now() - startTime, true);
      return cached;
    }

    try {
      // Perform attention computation based on mechanism
      const output = await this.performAttention(params);

      const latencyMs = performance.now() - startTime;

      const result: AttentionResult = {
        output,
        latencyMs,
        memoryBytes: this.estimateMemoryUsage(output),
        mechanism: this.config.mechanism,
        cacheHit: false,
      };

      // Update cache
      if (cacheKey) {
        this.updateCache(cacheKey, result);
      }

      // Update metrics
      this.updateMetrics(latencyMs, false);

      this.emit('attention-computed', {
        mechanism: this.config.mechanism,
        latencyMs
      });

      return result;
    } catch (error) {
      this.emit('attention-failed', { error });
      throw error;
    }
  }

  /**
   * Coordinate agent outputs using attention-based consensus
   *
   * This method uses attention mechanisms to weight and combine
   * multiple agent outputs into a consensus result.
   */
  async coordinateAgents<T>(params: {
    outputs: T[];
    embeddings: number[][];
    mechanism?: AttentionMechanism;
    topK?: number;
  }): Promise<{
    consensus: T;
    weights: number[];
    confidence: number;
  }> {
    this.ensureInitialized();

    const mechanism = params.mechanism || this.config.mechanism;
    const embeddings = params.embeddings;

    // Compute attention weights between all outputs
    const n = embeddings.length;
    const weights: number[] = new Array(n).fill(1 / n);

    if (n > 1) {
      // Compute pairwise attention scores
      const scores: number[] = [];
      for (let i = 0; i < n; i++) {
        let score = 0;
        for (let j = 0; j < n; j++) {
          if (i !== j) {
            score += this.dotProduct(embeddings[i], embeddings[j]);
          }
        }
        scores.push(score / (n - 1));
      }

      // Softmax to get weights
      const maxScore = Math.max(...scores);
      const expScores = scores.map(s => Math.exp(s - maxScore));
      const sumExp = expScores.reduce((a, b) => a + b, 0);

      for (let i = 0; i < n; i++) {
        weights[i] = expScores[i] / sumExp;
      }
    }

    // Select consensus (highest weighted output)
    const maxWeightIdx = weights.indexOf(Math.max(...weights));
    const consensus = params.outputs[maxWeightIdx];

    // Calculate confidence
    const confidence = weights[maxWeightIdx];

    this.emit('agents-coordinated', {
      agentCount: n,
      mechanism,
      confidence
    });

    return { consensus, weights, confidence };
  }

  /**
   * Route to experts using MoE attention
   */
  async routeToExperts<T>(params: {
    task: { embedding: number[] };
    experts: Array<{ id: string; embedding: number[] }>;
    topK?: number;
  }): Promise<Array<{ expertId: string; score: number }>> {
    this.ensureInitialized();

    const topK = params.topK || 3;
    const taskEmb = params.task.embedding;

    // Compute scores for each expert
    const scores = params.experts.map(expert => ({
      expertId: expert.id,
      score: this.cosineSimilarity(taskEmb, expert.embedding),
    }));

    // Sort and return top K
    scores.sort((a, b) => b.score - a.score);
    const topExperts = scores.slice(0, topK);

    this.emit('experts-routed', {
      expertCount: params.experts.length,
      topK,
      topExpert: topExperts[0]?.expertId
    });

    return topExperts;
  }

  /**
   * Get attention metrics
   */
  async getMetrics(): Promise<AttentionMetrics> {
    this.ensureInitialized();

    return { ...this.metrics };
  }

  /**
   * Get mechanism profile
   */
  getMechanismProfile(mechanism?: AttentionMechanism) {
    return MECHANISM_PROFILES[mechanism || this.config.mechanism];
  }

  /**
   * Suggest optimal mechanism for use case
   */
  suggestMechanism(useCase: string): AttentionMechanism {
    const lowerCase = useCase.toLowerCase();

    for (const [mechanism, profile] of Object.entries(MECHANISM_PROFILES)) {
      for (const match of profile.bestFor) {
        if (lowerCase.includes(match) || match.includes(lowerCase)) {
          return mechanism as AttentionMechanism;
        }
      }
    }

    // Default to flash attention
    return 'flash';
  }

  /**
   * Clear the attention cache
   */
  clearCache(): void {
    this.cache.clear();
    this.emit('cache-cleared');
  }

  /**
   * Shutdown the coordinator
   */
  async shutdown(): Promise<void> {
    this.cache.clear();
    this.initialized = false;
    this.emit('shutdown');
  }

  // ===== Private Methods =====

  private mergeConfig(config: Partial<AttentionConfiguration>): AttentionConfiguration {
    return {
      mechanism: config.mechanism || 'flash',
      numHeads: config.numHeads ?? 8,
      headDim: config.headDim ?? 64,
      dropoutRate: config.dropoutRate ?? 0.0,
      causalMask: config.causalMask ?? false,
      useRoPE: config.useRoPE ?? true,
      flashOptLevel: config.flashOptLevel ?? 2,
      memoryOptimization: config.memoryOptimization || 'moderate',
    };
  }

  private initializeMetrics(): AttentionMetrics {
    return {
      avgLatencyMs: 0,
      throughputTps: 0,
      memoryEfficiency: 1.0,
      cacheHitRate: 0,
      totalOperations: 0,
      speedupFactor: 1.0,
    };
  }

  private validateConfig(): void {
    if (this.config.numHeads <= 0) {
      throw new Error('numHeads must be positive');
    }
    if (this.config.headDim <= 0) {
      throw new Error('headDim must be positive');
    }
    if (this.config.dropoutRate < 0 || this.config.dropoutRate > 1) {
      throw new Error('dropoutRate must be between 0 and 1');
    }
    if (this.config.flashOptLevel < 0 || this.config.flashOptLevel > 3) {
      throw new Error('flashOptLevel must be between 0 and 3');
    }
  }

  private async prewarmCache(): Promise<void> {
    // Pre-compute common attention patterns
    // This is a no-op in the simplified implementation
  }

  /**
   * Perform attention computation
   *
   * ADR-001: For sequences longer than 512 tokens, delegates to
   * agentic-flow's native Flash Attention (approximate sparse attention;
   * speedup unverified — see docs/reviews/intelligence-system-audit-2026-05-29.md).
   */
  private async performAttention(params: {
    query: number[] | Float32Array;
    key: number[] | Float32Array;
    value: number[] | Float32Array;
    mask?: boolean[];
  }): Promise<number[]> {
    const { query, key, value, mask } = params;

    const qArray = Array.isArray(query) ? query : Array.from(query);
    const kArray = Array.isArray(key) ? key : Array.from(key);
    const vArray = Array.isArray(value) ? value : Array.from(value);

    // Calculate sequence length for delegation decision
    const sequenceLength = qArray.length;

    // ADR-001: Delegate to agentic-flow for long sequences.
    // Flash Attention is an approximate sparse path; speedup unverified —
    // see docs/reviews/intelligence-system-audit-2026-05-29.md
    if (
      this.isDelegationEnabled() &&
      this.agenticFlowAttention &&
      sequenceLength > DELEGATION_SEQUENCE_THRESHOLD
    ) {
      try {
        const result = await this.agenticFlowAttention.compute({
          query: qArray,
          key: kArray,
          value: vArray,
          mask,
          mechanism: this.config.mechanism,
        });

        this.emit('attention-delegated', {
          sequenceLength,
          mechanism: result.mechanism,
          latencyMs: result.latencyMs,
          target: 'agentic-flow',
        });

        return result.output;
      } catch (error) {
        // Log delegation failure and fall back to local implementation
        this.emit('delegation-failed', {
          method: 'performAttention',
          sequenceLength,
          error: (error as Error).message,
          fallback: 'local',
        });
        // Continue with local implementation below
      }
    }

    // Local implementation (fallback or for short sequences)
    // For short sequences, local JS implementation is sufficient
    // and avoids overhead of cross-boundary calls

    // Compute attention scores (Q * K^T)
    let score = this.dotProduct(qArray, kArray);

    // Scale by sqrt(d_k)
    score = score / Math.sqrt(this.config.headDim);

    // Apply softmax (simplified for single attention head)
    const weight = 1.0; // Math.exp(score) / Math.exp(score)

    // Compute output (weight * V)
    const output = vArray.map(v => v * weight);

    // Apply mechanism-specific optimizations
    switch (this.config.mechanism) {
      case 'flash':
        // Flash attention optimization: fused operations
        // For short sequences, the JS implementation is used
        // Native Flash Attention is used via delegation for longer sequences
        break;
      case 'linear':
        // Linear attention: O(n) instead of O(n^2)
        break;
      case 'sparse':
        // Sparse attention: only compute non-zero patterns
        break;
    }

    return output;
  }

  private computeCacheKey(params: {
    query: number[] | Float32Array;
    key: number[] | Float32Array;
    value: number[] | Float32Array;
  }): string {
    // Simple hash of first few elements
    const qHash = this.simpleHash(params.query);
    const kHash = this.simpleHash(params.key);
    const vHash = this.simpleHash(params.value);
    return `${this.config.mechanism}:${qHash}:${kHash}:${vHash}`;
  }

  private simpleHash(arr: number[] | Float32Array): number {
    const slice = Array.isArray(arr) ? arr.slice(0, 8) : Array.from(arr).slice(0, 8);
    let hash = 0;
    for (const v of slice) {
      hash = ((hash << 5) - hash) + Math.floor(v * 1000);
      hash = hash & hash;
    }
    return hash;
  }

  private updateCache(key: string, result: AttentionResult): void {
    if (this.cache.size >= this.maxCacheSize) {
      // Remove oldest entry
      const firstKey = this.cache.keys().next().value;
      if (firstKey !== undefined) {
        this.cache.delete(firstKey);
      }
    }
    this.cache.set(key, result);
  }

  private updateMetrics(latencyMs: number, cacheHit: boolean): void {
    this.operationCount++;
    this.totalLatencyMs += latencyMs;
    if (cacheHit) this.cacheHits++;

    this.metrics.avgLatencyMs = this.totalLatencyMs / this.operationCount;
    this.metrics.totalOperations = this.operationCount;
    this.metrics.cacheHitRate = this.cacheHits / this.operationCount;

    // Estimate throughput (tokens per second)
    if (this.metrics.avgLatencyMs > 0) {
      this.metrics.throughputTps = 1000 / this.metrics.avgLatencyMs;
    }

    // Speedup factor based on mechanism profile.
    // A [0, 0] speedupRange is the "unmeasured" sentinel (e.g. flash, where
    // no benchmark kernel is wired) — report 0 rather than fabricate a value.
    // See docs/reviews/intelligence-system-audit-2026-05-29.md
    const profile = MECHANISM_PROFILES[this.config.mechanism];
    const isUnmeasured = profile.speedupRange[0] === 0 && profile.speedupRange[1] === 0;
    this.metrics.speedupFactor = isUnmeasured
      ? 0 // unmeasured — no fabrication
      : (profile.speedupRange[0] + profile.speedupRange[1]) / 2;
    this.metrics.memoryEfficiency = 1 - profile.memoryReduction;
  }

  private estimateMemoryUsage(output: number[]): number {
    // Estimate: 8 bytes per float64
    return output.length * 8;
  }

  private dotProduct(a: number[], b: number[]): number {
    let sum = 0;
    const len = Math.min(a.length, b.length);
    for (let i = 0; i < len; i++) {
      sum += a[i] * b[i];
    }
    return sum;
  }

  private cosineSimilarity(a: number[], b: number[]): number {
    const dot = this.dotProduct(a, b);
    const normA = Math.sqrt(this.dotProduct(a, a));
    const normB = Math.sqrt(this.dotProduct(b, b));

    if (normA === 0 || normB === 0) return 0;
    return dot / (normA * normB);
  }

  private ensureInitialized(): void {
    if (!this.initialized) {
      throw new Error('AttentionCoordinator not initialized. Call initialize() first.');
    }
  }
}

/**
 * Create and initialize an AttentionCoordinator
 */
export async function createAttentionCoordinator(
  config?: Partial<AttentionConfiguration>
): Promise<AttentionCoordinator> {
  const coordinator = new AttentionCoordinator(config);
  await coordinator.initialize();
  return coordinator;
}
