export class FAISSIndex {
  private store: Map<string, number[]> = new Map();
  private readonly dimension = 192;
  private initialized = false;

  async initialize(): Promise<void> {
    if (this.initialized) return;
    this.initialized = true;
  }

  async add(id: string, vector: number[]): Promise<void> {
    this.normalizeInPlace(vector);
    this.store.set(id, [...vector]);
  }

  async remove(id: string): Promise<void> {
    this.store.delete(id);
  }

  async search(
    queryVector: number[],
    topK: number = 5
  ): Promise<SearchResult[]> {
    const normalized = [...queryVector];
    this.normalizeInPlace(normalized);

    const scores: Array<{ id: string; similarity: number }> = [];

    for (const [id, vector] of this.store.entries()) {
      const similarity = this.cosineSimilarity(normalized, vector);
      scores.push({ id, similarity });
    }

    scores.sort((a, b) => b.similarity - a.similarity);
    return scores.slice(0, topK).map((s, i) => ({ rank: i + 1, id: s.id, similarity: s.similarity }));
  }

  async count(): Promise<number> {
    return this.store.size;
  }

  async clear(): Promise<void> {
    this.store.clear();
  }

  async loadFromDatabase(): Promise<void> {
    const prisma = (await import("@shieldai/db")).default;
    const enrollments = await prisma.voiceEnrollment.findMany({
      select: { id: true, embeddingVector: true },
    });

    for (const enrollment of enrollments) {
      this.store.set(enrollment.id, enrollment.embeddingVector);
    }
  }

  private cosineSimilarity(a: number[], b: number[]): number {
    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];
    }

    const denominator = Math.sqrt(normA) * Math.sqrt(normB);
    return denominator > 0 ? dotProduct / denominator : 0;
  }

  private normalizeInPlace(vector: number[]): void {
    const norm = Math.sqrt(vector.reduce((s, v) => s + v * v, 0));
    if (norm > 0) {
      for (let i = 0; i < vector.length; i++) {
        vector[i] /= norm;
      }
    }
  }
}

export interface SearchResult {
  rank: number;
  id: string;
  similarity: number;
}
