// Local embedding generation using transformers.js
// Constraint: No API calls, local-only (spec.md:64)
// Model: Xenova/all-MiniLM-L6-v2 (80MB, 384-dim) (spec.md:48)

import { pipeline, FeatureExtractionPipeline } from "@xenova/transformers";

let embeddingPipeline: FeatureExtractionPipeline | null = null;

/**
 * Initialize the embedding model (lazy-loaded, cached)
 * Downloads ~80MB on first run to ~/.cache/transformers
 */
async function getEmbeddingPipeline(): Promise<FeatureExtractionPipeline> {
  if (!embeddingPipeline) {
    embeddingPipeline = await pipeline(
      "feature-extraction",
      "Xenova/all-MiniLM-L6-v2",
    );
  }
  return embeddingPipeline;
}

/**
 * Generate embeddings for a single text
 * @param text - Input text (max 512 tokens, auto-truncated)
 * @returns 384-dimensional embedding vector
 */
export async function generateEmbedding(text: string): Promise<number[]> {
  const pipe = await getEmbeddingPipeline();

  // Truncate to 512 tokens if needed (Edge Case #4, spec.md:111)
  const truncatedText = text.slice(0, 2048); // ~512 tokens

  const output = await pipe(truncatedText, {
    pooling: "mean",
    normalize: true,
  });

  // Extract embedding array (384 floats)
  return Array.from(output.data as Float32Array);
}

/**
 * Compute cosine similarity between two embeddings
 * @param a - First embedding vector
 * @param b - Second embedding vector
 * @returns Similarity score (0-1, higher = more similar)
 */
export function cosineSimilarity(a: number[], b: number[]): number {
  if (a.length !== b.length) {
    throw new Error("Embeddings must have same dimension");
  }

  let dotProduct = 0;
  let normA = 0;
  let normB = 0;

  for (let i = 0; i < a.length; i++) {
    dotProduct += a[i] * b[i];
    normA += a[i] * a[i];
    normB += b[i] * b[i];
  }

  return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
}
