// LOCKED: TRUE
// All content in this file is locked and cannot be edited.

import { SemanticCacheEntry, SemanticCacheManager } from "../types/cache";
import { EmbeddingProvider } from "../types/provider";

export class SemanticCacheManagerImpl implements SemanticCacheManager {
  private cache: Map<string, SemanticCacheEntry> = new Map();
  private embeddingProvider: EmbeddingProvider;

  constructor(embeddingProvider: EmbeddingProvider) {
    this.embeddingProvider = embeddingProvider;
  }

  async get(key: string): Promise<any> {
    const entry = this.cache.get(key);
    if (entry && (!entry.expiresAt || entry.expiresAt > Date.now())) {
      return entry.value;
    }
    return undefined;
  }

  async set(key: string, value: any, ttl?: number): Promise<void> {
    const embedding = await this.embeddingProvider.embed(key);
    const entry: SemanticCacheEntry = {
      key,
      value,
      embedding,
      expiresAt: ttl ? Date.now() + ttl : undefined,
    };
    this.cache.set(key, entry);
  }

  async delete(key: string): Promise<void> {
    this.cache.delete(key);
  }

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

  async search(query: string, threshold: number): Promise<SemanticCacheEntry[]> {
    const queryEmbedding = await this.embeddingProvider.embed(query);
    const results: SemanticCacheEntry[] = [];

    for (const entry of this.cache.values()) {
      if (entry.expiresAt && entry.expiresAt <= Date.now()) {
        continue;
      }

      const similarity = this.cosineSimilarity(queryEmbedding, entry.embedding);
      if (similarity >= threshold) {
        results.push(entry);
      }
    }

    return results.sort((a, b) => 
      this.cosineSimilarity(queryEmbedding, b.embedding) - 
      this.cosineSimilarity(queryEmbedding, a.embedding)
    );
  }

  private cosineSimilarity(a: number[], b: number[]): number {
    const dotProduct = a.reduce((sum, _, i) => sum + a[i] * b[i], 0);
    const magnitudeA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0));
    const magnitudeB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0));
    return dotProduct / (magnitudeA * magnitudeB);
  }
}