import { logger } from "../monitoring/logger.js";
class ContextRetriever {
  adapter;
  strategies = /* @__PURE__ */ new Map();
  queryCache = /* @__PURE__ */ new Map();
  cacheMaxSize = 100;
  cacheExpiryMs = 3e5;
  // 5 minutes
  constructor(adapter) {
    this.adapter = adapter;
    this.initializeStrategies();
  }
  initializeStrategies() {
    this.strategies.set("keyword", {
      name: "Keyword Search",
      searchType: "text",
      boost: {
        name: 2,
        digest_text: 1.5,
        inputs: 1.2,
        outputs: 1.2
      },
      fallbackStrategy: "semantic"
    });
    this.strategies.set("semantic", {
      name: "Semantic Search",
      searchType: "vector",
      fallbackStrategy: "hybrid"
    });
    this.strategies.set("hybrid", {
      name: "Hybrid Search",
      searchType: "hybrid",
      weights: { text: 0.6, vector: 0.4 },
      boost: {
        name: 2,
        digest_text: 1.5
      },
      fallbackStrategy: "keyword"
    });
    this.strategies.set("recent", {
      name: "Recent Activity",
      searchType: "text",
      boost: {
        created_at: 3,
        closed_at: 2
      },
      fallbackStrategy: "hybrid"
    });
    this.strategies.set("debug", {
      name: "Debug Context",
      searchType: "hybrid",
      weights: { text: 0.8, vector: 0.2 },
      boost: {
        type: 2.5,
        // Boost error frames
        digest_text: 2,
        outputs: 1.8
      },
      fallbackStrategy: "keyword"
    });
  }
  async retrieveContext(query) {
    const startTime = Date.now();
    if (!query.text || query.text.trim().length === 0) {
      logger.debug("Empty query provided, returning empty result");
      return {
        contexts: [],
        totalMatches: 0,
        retrievalTimeMs: Date.now() - startTime,
        strategy: "empty_query",
        queryAnalysis: {
          intent: "general",
          concepts: [],
          complexity: "simple"
        }
      };
    }
    const cacheKey = this.generateCacheKey(query);
    const cached = this.getCachedResult(cacheKey);
    if (cached) {
      logger.debug("Context retrieval cache hit");
      return cached;
    }
    try {
      logger.info("Starting LLM-driven context retrieval", {
        query: query.text
      });
      const queryAnalysis = await this.analyzeQuery(query);
      const strategy = this.selectStrategy(queryAnalysis, query);
      logger.debug("Selected retrieval strategy", {
        strategy: strategy.name,
        analysis: queryAnalysis
      });
      const contexts = await this.executeRetrieval(
        query,
        strategy,
        queryAnalysis
      );
      const rankedContexts = await this.rankAndFilter(
        contexts,
        query,
        queryAnalysis
      );
      const result = {
        contexts: rankedContexts,
        totalMatches: contexts.length,
        retrievalTimeMs: Date.now() - startTime,
        strategy: strategy.name,
        queryAnalysis
      };
      this.cacheResult(cacheKey, result);
      logger.info("Context retrieval completed", {
        resultsCount: rankedContexts.length,
        timeMs: result.retrievalTimeMs,
        strategy: strategy.name
      });
      return result;
    } catch (error) {
      logger.error("Context retrieval failed:", error);
      return {
        contexts: [],
        totalMatches: 0,
        retrievalTimeMs: Date.now() - startTime,
        strategy: "fallback",
        queryAnalysis: {
          intent: "unknown",
          concepts: [],
          complexity: "simple"
        }
      };
    }
  }
  async analyzeQuery(query) {
    const text = query.text.toLowerCase().trim();
    const words = text.split(/\s+/);
    let intent = "general";
    if (this.containsKeywords(text, [
      "error",
      "exception",
      "fail",
      "bug",
      "issue",
      "problem",
      "debug"
    ])) {
      intent = "debug";
    } else if (this.containsKeywords(text, ["how", "what", "why", "when", "where"])) {
      intent = "explanation";
    } else if (this.containsKeywords(text, [
      "implement",
      "create",
      "build",
      "add",
      "develop"
    ])) {
      intent = "implementation";
    } else if (this.containsKeywords(text, [
      "recent",
      "latest",
      "last",
      "current",
      "happened"
    ])) {
      intent = "recent_activity";
    }
    const concepts = this.extractConcepts(text);
    let complexity = "simple";
    if (words.length > 10 || concepts.length > 5) {
      complexity = "complex";
    } else if (words.length > 5 || concepts.length > 2) {
      complexity = "moderate";
    }
    return { intent, concepts, complexity };
  }
  containsKeywords(text, keywords) {
    return keywords.some(
      (keyword) => text.toLowerCase().includes(keyword.toLowerCase())
    );
  }
  extractConcepts(text) {
    const technicalTerms = [
      "database",
      "sql",
      "query",
      "index",
      "migration",
      "adapter",
      "frame",
      "event",
      "anchor",
      "digest",
      "context",
      "search",
      "vector",
      "embedding",
      "similarity",
      "score",
      "rank",
      "performance",
      "optimization",
      "cache",
      "pool",
      "connection",
      "error",
      "exception",
      "debug",
      "trace",
      "log",
      "monitor"
    ];
    const concepts = [];
    const words = text.split(/\W+/).map((w) => w.toLowerCase());
    for (const term of technicalTerms) {
      if (words.includes(term)) {
        concepts.push(term);
      }
    }
    const bigrams = this.extractBigrams(words);
    const technicalBigrams = [
      "database adapter",
      "query router",
      "connection pool",
      "vector search"
    ];
    for (const bigram of bigrams) {
      if (technicalBigrams.includes(bigram)) {
        concepts.push(bigram);
      }
    }
    return [...new Set(concepts)];
  }
  extractBigrams(words) {
    const bigrams = [];
    for (let i = 0; i < words.length - 1; i++) {
      bigrams.push(`${words[i]} ${words[i + 1]}`);
    }
    return bigrams;
  }
  selectStrategy(analysis, query) {
    if (query.type) {
      return this.strategies.get(
        query.type === "keyword" ? "keyword" : query.type === "semantic" ? "semantic" : "hybrid"
      ) || this.strategies.get("hybrid");
    }
    switch (analysis.intent) {
      case "debug":
        return this.strategies.get("debug");
      case "recent_activity":
        return this.strategies.get("recent");
      case "explanation":
        return analysis.complexity === "simple" ? this.strategies.get("keyword") : this.strategies.get("semantic");
      case "implementation":
        return this.strategies.get("hybrid");
      default:
        return analysis.complexity === "complex" ? this.strategies.get("semantic") : this.strategies.get("keyword");
    }
  }
  async executeRetrieval(query, strategy, analysis) {
    const searchOptions = {
      query: query.text,
      searchType: strategy.searchType,
      limit: query.maxResults || 20,
      scoreThreshold: query.scoreThreshold || 0.1,
      boost: strategy.boost
    };
    if (query.frameTypes) {
      searchOptions.fields = ["type", "name", "digest_text"];
    }
    let rawResults = [];
    try {
      if (strategy.searchType === "hybrid" && strategy.weights) {
        const embedding = await this.generateEmbedding(query.text);
        rawResults = await this.adapter.searchHybrid(
          query.text,
          embedding,
          strategy.weights
        );
      } else {
        rawResults = await this.adapter.search(searchOptions);
      }
    } catch (error) {
      logger.warn(`Strategy ${strategy.name} failed, trying fallback:`, error);
      if (strategy.fallbackStrategy) {
        const fallbackStrategy = this.strategies.get(strategy.fallbackStrategy);
        if (fallbackStrategy) {
          return this.executeRetrieval(query, fallbackStrategy, analysis);
        }
      }
      return [];
    }
    return rawResults.map((result) => ({
      frame: result,
      score: result.score,
      relevanceReason: this.generateRelevanceReason(result, query, analysis),
      retrievalMethod: strategy.searchType,
      matchedFields: this.identifyMatchedFields(result, query)
    }));
  }
  async generateEmbedding(text) {
    const hash = this.simpleHash(text);
    return Array.from(
      { length: 384 },
      (_, i) => (hash + i) % 100 / 100 - 0.5
    );
  }
  simpleHash(str) {
    let hash = 0;
    for (let i = 0; i < str.length; i++) {
      const char = str.charCodeAt(i);
      hash = (hash << 5) - hash + char;
      hash = hash & hash;
    }
    return Math.abs(hash);
  }
  generateRelevanceReason(frame, query, analysis) {
    const reasons = [];
    if (frame.name.toLowerCase().includes(query.text.toLowerCase())) {
      reasons.push("Frame name matches query");
    }
    if (frame.digest_text?.toLowerCase().includes(query.text.toLowerCase())) {
      reasons.push("Content contains query terms");
    }
    for (const concept of analysis.concepts) {
      if (frame.digest_text?.toLowerCase().includes(concept.toLowerCase()) || frame.name.toLowerCase().includes(concept.toLowerCase())) {
        reasons.push(`Related to ${concept}`);
      }
    }
    if (analysis.intent === "debug" && frame.type.includes("error")) {
      reasons.push("Error context for debugging");
    }
    return reasons.length > 0 ? reasons.join("; ") : "General semantic similarity";
  }
  identifyMatchedFields(frame, query) {
    const matched = [];
    const queryLower = query.text.toLowerCase();
    if (frame.name.toLowerCase().includes(queryLower)) {
      matched.push("name");
    }
    if (frame.digest_text?.toLowerCase().includes(queryLower)) {
      matched.push("digest_text");
    }
    if (frame.type.toLowerCase().includes(queryLower)) {
      matched.push("type");
    }
    return matched;
  }
  async rankAndFilter(contexts, query, analysis) {
    let filtered = contexts;
    if (query.timeRange) {
      filtered = filtered.filter((ctx) => {
        const frameTime = new Date(ctx.frame.created_at);
        const start = query.timeRange?.start;
        const end = query.timeRange?.end;
        return (!start || frameTime >= start) && (!end || frameTime <= end);
      });
    }
    if (query.frameTypes) {
      filtered = filtered.filter(
        (ctx) => query.frameTypes.includes(ctx.frame.type)
      );
    }
    if (query.scoreThreshold) {
      filtered = filtered.filter((ctx) => ctx.score >= query.scoreThreshold);
    }
    const ranked = filtered.map((ctx) => ({
      ...ctx,
      score: this.calculateEnhancedScore(ctx, query, analysis)
    }));
    ranked.sort((a, b) => b.score - a.score);
    const maxResults = query.maxResults || 20;
    return ranked.slice(0, maxResults);
  }
  calculateEnhancedScore(context, query, analysis) {
    let score = context.score;
    const ageHours = (Date.now() - context.frame.created_at) / (1e3 * 60 * 60);
    if (ageHours < 24) {
      score *= 1.2;
    } else if (ageHours < 168) {
      score *= 1.1;
    }
    if (context.frame.closed_at) {
      score *= 1.1;
    }
    if (analysis.intent === "debug" && context.frame.type.includes("error")) {
      score *= 1.5;
    }
    if (context.matchedFields.includes("name")) {
      score *= 1.3;
    }
    if (context.matchedFields.length > 1) {
      score *= 1.1;
    }
    if (analysis.intent === "recent_activity" && ageHours > 168) {
      score *= 0.5;
    }
    return score;
  }
  generateCacheKey(query) {
    return JSON.stringify({
      text: query.text,
      type: query.type,
      maxResults: query.maxResults,
      frameTypes: query.frameTypes,
      scoreThreshold: query.scoreThreshold
    });
  }
  getCachedResult(cacheKey) {
    const entry = this.queryCache.get(cacheKey);
    if (!entry) return null;
    return entry;
  }
  cacheResult(cacheKey, result) {
    if (this.queryCache.size >= this.cacheMaxSize) {
      const firstKey = this.queryCache.keys().next().value;
      this.queryCache.delete(firstKey);
    }
    this.queryCache.set(cacheKey, result);
  }
  // Utility methods for integration
  async findSimilarFrames(frameId, limit = 10) {
    const frame = await this.adapter.getFrame(frameId);
    if (!frame) {
      throw new Error(`Frame not found: ${frameId}`);
    }
    const query = {
      text: frame.digest_text || frame.name,
      type: "semantic",
      maxResults: limit,
      scoreThreshold: 0.3
    };
    const result = await this.retrieveContext(query);
    return result.contexts.filter((ctx) => ctx.frame.frame_id !== frameId);
  }
  async findContextForError(errorMessage, stackTrace) {
    const query = {
      text: `${errorMessage} ${stackTrace || ""}`.trim(),
      type: "hybrid",
      maxResults: 15,
      frameTypes: ["error", "debug", "function"],
      scoreThreshold: 0.2
    };
    const result = await this.retrieveContext(query);
    return result.contexts;
  }
  async getRecentContext(hours = 24, frameTypes) {
    const query = {
      text: "recent activity context",
      type: "keyword",
      maxResults: 50,
      timeRange: {
        start: new Date(Date.now() - hours * 60 * 60 * 1e3)
      },
      frameTypes,
      scoreThreshold: 0.1
    };
    const result = await this.retrieveContext(query);
    return result.contexts;
  }
  // Analytics and insights
  getRetrievalStats() {
    return {
      cacheSize: this.queryCache.size,
      strategiesCount: this.strategies.size,
      availableStrategies: Array.from(this.strategies.keys())
    };
  }
  clearCache() {
    this.queryCache.clear();
    logger.info("Context retrieval cache cleared");
  }
}
export {
  ContextRetriever
};
//# sourceMappingURL=context-retriever.js.map
