/**
 * Codebase RAG System
 *
 * Main RAG (Retrieval-Augmented Generation) system for codebase search.
 * Implements semantic code search with intelligent chunking and retrieval.
 *
 * Based on research from:
 * - Corrective RAG (CRAG, 2024)
 * - RAG for Large Scale Code Repos (Qodo)
 * - RAG Comprehensive Survey (arXiv 2506.00054)
 */
import { promises as fsPromises } from "fs";
import path from "path";
import { EventEmitter } from "events";
import { logger } from "../../utils/logger.js";
import { DEFAULT_RAG_CONFIG, } from "./types.js";
import { createChunker, detectLanguage } from "./chunker.js";
import { createEmbeddingProvider } from "./embeddings.js";
import { createVectorStore, InMemoryVectorStore } from "./vector-store.js";
import { getErrorMessage } from "../../types/index.js";
import { getCrossEncoderReranker } from "../cross-encoder-reranker.js";
import { getMetrics } from "../../metrics/metrics-collector.js";
/**
 * Main Codebase RAG class
 */
export class CodebaseRAG extends EventEmitter {
    config;
    chunker;
    embedder;
    vectorStore;
    chunkStore = new Map();
    fileIndex = new Map(); // filePath -> chunkIds
    indexStats;
    isIndexing = false;
    rerankingLatencyHistogram = null;
    lastRerankingStats = null;
    constructor(config = {}) {
        super();
        this.config = { ...DEFAULT_RAG_CONFIG, ...config };
        this.chunker = createChunker(this.config);
        this.embedder = createEmbeddingProvider("code", this.config.embeddingDimension);
        this.vectorStore = createVectorStore("memory", {
            persistPath: this.config.indexPath
                ? path.join(this.config.indexPath, "vectors.json")
                : undefined,
        });
        this.indexStats = {
            totalChunks: 0,
            totalFiles: 0,
            totalTokens: 0,
            indexSize: 0,
            lastUpdated: new Date(),
            languages: {},
            chunkTypes: {},
        };
        // Initialize reranking latency metrics
        this.initializeRerankingMetrics();
    }
    /**
     * Initialize reranking latency metrics histogram
     */
    initializeRerankingMetrics() {
        const metrics = getMetrics();
        if (metrics) {
            this.rerankingLatencyHistogram = metrics.createHistogram('codebuddy_rag_reranking_latency_seconds', 'RAG reranking latency in seconds', [0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5], 'seconds');
        }
    }
    /**
     * Index an entire codebase
     */
    async indexCodebase(rootPath, options = {}) {
        if (this.isIndexing) {
            throw new Error("Indexing already in progress");
        }
        this.isIndexing = true;
        this.emit("index:start", { rootPath });
        try {
            // Find all files
            const files = await this.findFiles(rootPath, {
                includePatterns: options.includePatterns,
                excludePatterns: options.excludePatterns || this.config.excludePatterns,
            });
            this.emit("index:files_found", { count: files.length });
            // Index each file
            let processed = 0;
            for (const file of files) {
                const result = await this.indexFile(file);
                processed++;
                if (options.onProgress) {
                    options.onProgress(processed, files.length, file);
                }
                this.emit("index:file_processed", {
                    file,
                    success: result.success,
                    chunks: result.chunks.length,
                    progress: processed / files.length,
                });
            }
            // Update stats
            this.indexStats.lastUpdated = new Date();
            this.indexStats.totalFiles = this.fileIndex.size;
            this.indexStats.totalChunks = this.chunkStore.size;
            // Save to disk if configured
            if (this.config.indexPath) {
                await this.saveIndex();
            }
            this.emit("index:complete", { stats: this.indexStats });
            return this.indexStats;
        }
        finally {
            this.isIndexing = false;
        }
    }
    /**
     * Index a single file
     */
    async indexFile(filePath) {
        const startTime = Date.now();
        try {
            // Read file content
            const content = await fsPromises.readFile(filePath, "utf-8");
            // Skip binary files
            if (this.isBinaryContent(content)) {
                return {
                    filePath,
                    chunks: [],
                    success: false,
                    error: "Binary file",
                    processingTime: Date.now() - startTime,
                };
            }
            // Chunk the file
            const chunks = this.chunker.chunkFile(content, filePath);
            // Remove old chunks for this file
            await this.removeFileChunks(filePath);
            // Embed and store chunks
            const chunkIds = [];
            for (const chunk of chunks) {
                // Generate embedding
                const embedding = await this.embedder.embed(this.prepareTextForEmbedding(chunk));
                chunk.embedding = embedding;
                // Store in vector store
                await this.vectorStore.add(chunk.id, embedding, {
                    filePath: chunk.filePath,
                    type: chunk.type,
                    language: chunk.language,
                    startLine: chunk.startLine,
                    name: chunk.metadata.name,
                });
                // Store chunk
                this.chunkStore.set(chunk.id, chunk);
                chunkIds.push(chunk.id);
                // Update stats
                const lang = chunk.language;
                this.indexStats.languages[lang] = (this.indexStats.languages[lang] || 0) + 1;
                this.indexStats.chunkTypes[chunk.type] = (this.indexStats.chunkTypes[chunk.type] || 0) + 1;
                this.indexStats.totalTokens += this.estimateTokens(chunk.content);
            }
            // Update file index
            this.fileIndex.set(filePath, chunkIds);
            return {
                filePath,
                chunks,
                success: true,
                processingTime: Date.now() - startTime,
            };
        }
        catch (error) {
            return {
                filePath,
                chunks: [],
                success: false,
                error: getErrorMessage(error),
                processingTime: Date.now() - startTime,
            };
        }
    }
    /**
     * Retrieve relevant code chunks for a query
     */
    async retrieve(query, options = {}) {
        const startTime = Date.now();
        const topK = options.topK || this.config.topK;
        const minScore = options.minScore || this.config.minScore;
        const strategy = options.strategy || this.config.strategy;
        // Classify query intent (reserved for future intent-based filtering)
        const _intent = this.classifyQueryIntent(query);
        // Build filter from options
        const filter = this.buildFilter(options.filters);
        let results;
        switch (strategy) {
            case "semantic":
                results = await this.semanticSearch(query, topK * 2, filter);
                break;
            case "keyword":
                results = await this.keywordSearch(query, topK * 2, filter);
                break;
            case "hybrid":
                results = await this.hybridSearch(query, topK * 2, filter);
                break;
            case "reranked":
                results = await this.rerankedSearch(query, topK * 2, filter);
                break;
            case "corrective":
                results = await this.correctiveSearch(query, topK * 2, filter);
                break;
            default:
                results = await this.hybridSearch(query, topK * 2, filter);
        }
        // Filter by minimum score and take top K
        const filteredResults = results
            .filter(r => r.score >= minScore)
            .slice(0, topK);
        return {
            chunks: filteredResults,
            query,
            totalChunks: this.chunkStore.size,
            retrievalTime: Date.now() - startTime,
            strategy,
        };
    }
    /**
     * Semantic search using embeddings
     */
    async semanticSearch(query, k, filter) {
        const queryEmbedding = await this.embedder.embed(query);
        const searchResults = await this.vectorStore.search(queryEmbedding, k, filter);
        const results = [];
        for (const result of searchResults) {
            const chunk = this.chunkStore.get(result.id);
            if (chunk) {
                results.push({
                    chunk,
                    score: result.score,
                    matchType: "semantic",
                });
            }
        }
        return results;
    }
    /**
     * Keyword-based search
     */
    async keywordSearch(query, k, filter) {
        const queryTokens = this.tokenize(query.toLowerCase());
        const results = [];
        for (const chunk of this.chunkStore.values()) {
            // Apply filter
            if (filter && !this.matchesFilter(chunk, filter)) {
                continue;
            }
            const content = chunk.content.toLowerCase();
            const name = chunk.metadata.name?.toLowerCase() || "";
            // Calculate keyword score
            let score = 0;
            for (const token of queryTokens) {
                // Boost for name matches
                if (name.includes(token)) {
                    score += 2;
                }
                // Count content occurrences
                const regex = new RegExp(token, "gi");
                const matches = content.match(regex);
                if (matches) {
                    score += matches.length * 0.5;
                }
            }
            if (score > 0) {
                // Normalize score
                const normalizedScore = Math.min(score / queryTokens.length, 1);
                results.push({
                    chunk,
                    score: normalizedScore,
                    matchType: "keyword",
                    highlights: this.findHighlights(content, queryTokens),
                });
            }
        }
        return results
            .sort((a, b) => b.score - a.score)
            .slice(0, k);
    }
    /**
     * Hybrid search combining semantic and keyword
     */
    async hybridSearch(query, k, filter) {
        // Get both result sets
        const [semanticResults, keywordResults] = await Promise.all([
            this.semanticSearch(query, k, filter),
            this.keywordSearch(query, k, filter),
        ]);
        // Merge and deduplicate
        const merged = new Map();
        // Weight: 60% semantic, 40% keyword
        const semanticWeight = 0.6;
        const keywordWeight = 0.4;
        for (const result of semanticResults) {
            merged.set(result.chunk.id, {
                ...result,
                score: result.score * semanticWeight,
                matchType: "hybrid",
            });
        }
        for (const result of keywordResults) {
            const existing = merged.get(result.chunk.id);
            if (existing) {
                existing.score += result.score * keywordWeight;
                existing.highlights = result.highlights;
            }
            else {
                merged.set(result.chunk.id, {
                    ...result,
                    score: result.score * keywordWeight,
                    matchType: "hybrid",
                });
            }
        }
        return Array.from(merged.values())
            .sort((a, b) => b.score - a.score)
            .slice(0, k);
    }
    /**
     * Reranked search with cross-encoder scoring
     *
     * This method implements a two-stage retrieval process:
     * 1. Fast bi-encoder retrieval via hybrid search (top-20 candidates)
     * 2. Cross-encoder reranking for improved relevance (top-5 results)
     *
     * The cross-encoder jointly encodes query and document for more accurate
     * relevance scoring compared to independent embedding similarity.
     *
     * Research basis:
     * - "Sentence-BERT" (Reimers & Gurevych, 2019): Cross-encoder architecture
     * - Expected 25-35% improvement in retrieval quality
     */
    async rerankedSearch(query, k, filter) {
        // Check if reranking is enabled via config
        if (!this.config.useReranking) {
            // Fall back to hybrid search if reranking is disabled
            return this.hybridSearch(query, k, filter);
        }
        const rerankStartTime = Date.now();
        // Stage 1: Get top-20 candidates from hybrid search (configurable via rerankTopK)
        const candidateCount = this.config.rerankTopK || 20;
        const candidates = await this.hybridSearch(query, candidateCount, filter);
        if (candidates.length === 0) {
            return [];
        }
        // Stage 2: Cross-encoder reranking
        // Get the cross-encoder reranker with configuration for top-5 results
        const finalTopK = Math.min(k, 5); // Return top-5 after reranking (or k if smaller)
        const reranker = getCrossEncoderReranker({
            maxCandidates: candidateCount,
            topK: finalTopK,
            minScore: this.config.minScore,
            biEncoderWeight: 0.3, // 30% weight to original bi-encoder score
            crossEncoderWeight: 0.7, // 70% weight to cross-encoder score
            enableQueryExpansion: true,
        });
        // Perform reranking
        const { results: rerankedResults, stats } = reranker.rerank(query, candidates);
        // Store stats for later retrieval
        this.lastRerankingStats = stats;
        // Record latency metrics
        const rerankLatencyMs = Date.now() - rerankStartTime;
        const rerankLatencySec = rerankLatencyMs / 1000;
        if (this.rerankingLatencyHistogram) {
            this.rerankingLatencyHistogram.observe(rerankLatencySec, {
                candidates: String(candidates.length),
                results: String(rerankedResults.length),
            });
        }
        // Emit reranking event for observability
        this.emit("rerank:complete", {
            query,
            candidateCount: candidates.length,
            resultCount: rerankedResults.length,
            latencyMs: rerankLatencyMs,
            avgScoreImprovement: this.calculateScoreImprovement(rerankedResults),
            stats,
        });
        // Log reranking stats
        logger.debug("RAG reranking complete", {
            query: query.substring(0, 50),
            candidatesIn: candidates.length,
            resultsOut: rerankedResults.length,
            latencyMs: rerankLatencyMs,
            avgOriginalScore: stats.avgOriginalScore.toFixed(3),
            avgCrossEncoderScore: stats.avgCrossEncoderScore.toFixed(3),
            avgFinalScore: stats.avgFinalScore.toFixed(3),
        });
        // Convert RerankedResult back to ScoredChunk format
        return rerankedResults.map(result => ({
            chunk: result.chunk,
            score: result.finalScore,
            matchType: "reranked",
            highlights: this.findHighlights(result.chunk.content.toLowerCase(), this.tokenize(query.toLowerCase())),
        }));
    }
    /**
     * Calculate average score improvement from reranking
     */
    calculateScoreImprovement(rerankedResults) {
        if (rerankedResults.length === 0)
            return 0;
        // Calculate average improvement for reranked results
        const improvements = rerankedResults.map(r => r.finalScore - r.originalScore);
        return improvements.reduce((sum, imp) => sum + imp, 0) / improvements.length;
    }
    /**
     * Get the last reranking statistics
     */
    getLastRerankingStats() {
        return this.lastRerankingStats;
    }
    /**
     * Corrective RAG search
     * Evaluates relevance and refines query if needed
     */
    async correctiveSearch(query, k, filter, iteration = 0) {
        const maxIterations = 3;
        // Get initial results
        const results = await this.hybridSearch(query, k, filter);
        if (iteration >= maxIterations || results.length === 0) {
            return results;
        }
        // Evaluate relevance of top results
        const evaluation = this.evaluateRelevance(query, results.slice(0, 3));
        if (evaluation.isRelevant && evaluation.confidence > 0.7) {
            return results;
        }
        // Try to refine the query based on evaluation
        if (evaluation.action === "refine" && evaluation.refinedQuery) {
            return this.correctiveSearch(evaluation.refinedQuery, k, filter, iteration + 1);
        }
        return results;
    }
    /**
     * Evaluate relevance of retrieved chunks
     */
    evaluateRelevance(query, topResults) {
        if (topResults.length === 0) {
            return {
                isRelevant: false,
                confidence: 0,
                action: "reject",
                feedback: "No results found",
            };
        }
        // Calculate average score
        const avgScore = topResults.reduce((sum, r) => sum + r.score, 0) / topResults.length;
        // Check if query terms appear in results
        const queryTokens = this.tokenize(query.toLowerCase());
        let termMatches = 0;
        for (const result of topResults) {
            const content = result.chunk.content.toLowerCase();
            for (const token of queryTokens) {
                if (content.includes(token)) {
                    termMatches++;
                }
            }
        }
        const termMatchRatio = termMatches / (queryTokens.length * topResults.length);
        // Determine relevance
        const isRelevant = avgScore > 0.5 && termMatchRatio > 0.3;
        const confidence = (avgScore + termMatchRatio) / 2;
        let action = "accept";
        let refinedQuery;
        if (!isRelevant) {
            if (termMatchRatio < 0.1) {
                // Try expanding the query
                action = "refine";
                refinedQuery = this.expandQuery(query);
            }
            else {
                action = "reject";
            }
        }
        return {
            isRelevant,
            confidence,
            action,
            refinedQuery,
        };
    }
    /**
     * Expand query with related terms
     */
    expandQuery(query) {
        // Simple query expansion using synonyms and related terms
        const expansions = {
            function: ["method", "func", "def", "procedure"],
            class: ["struct", "interface", "type"],
            error: ["exception", "throw", "catch", "fail"],
            test: ["spec", "describe", "it", "expect"],
            api: ["endpoint", "route", "handler"],
            database: ["db", "sql", "query", "model"],
            auth: ["authentication", "login", "session", "token"],
        };
        const tokens = this.tokenize(query.toLowerCase());
        const expanded = [...tokens];
        for (const token of tokens) {
            const related = expansions[token];
            if (related) {
                expanded.push(...related.slice(0, 2));
            }
        }
        return expanded.join(" ");
    }
    /**
     * Classify query intent
     */
    classifyQueryIntent(query) {
        const lowerQuery = query.toLowerCase();
        let type = "general";
        const entities = [];
        // Detect intent type
        if (/find|search|where|locate/.test(lowerQuery)) {
            type = "find_function";
        }
        else if (/how|what|explain|understand/.test(lowerQuery)) {
            type = "understand_code";
        }
        else if (/fix|bug|error|issue/.test(lowerQuery)) {
            type = "fix_bug";
        }
        else if (/add|implement|create|feature/.test(lowerQuery)) {
            type = "add_feature";
        }
        else if (/refactor|improve|optimize/.test(lowerQuery)) {
            type = "refactor";
        }
        // Extract entities (function/class names)
        const namePattern = /(?:function|class|method|interface|type)\s+`?(\w+)`?/gi;
        let match;
        while ((match = namePattern.exec(query)) !== null) {
            entities.push(match[1]);
        }
        // Also extract backtick-quoted names
        const backtickPattern = /`(\w+)`/g;
        while ((match = backtickPattern.exec(query)) !== null) {
            if (!entities.includes(match[1])) {
                entities.push(match[1]);
            }
        }
        return {
            type,
            entities,
            confidence: entities.length > 0 ? 0.8 : 0.5,
        };
    }
    /**
     * Prepare text for embedding (combines metadata with content)
     */
    prepareTextForEmbedding(chunk) {
        const parts = [];
        if (chunk.metadata.name) {
            parts.push(`Name: ${chunk.metadata.name}`);
        }
        if (chunk.metadata.signature) {
            parts.push(`Signature: ${chunk.metadata.signature}`);
        }
        if (chunk.metadata.docstring) {
            parts.push(`Documentation: ${chunk.metadata.docstring}`);
        }
        parts.push(chunk.content);
        return parts.join("\n");
    }
    /**
     * Build filter object from QueryFilters
     */
    buildFilter(filters) {
        if (!filters)
            return undefined;
        const filter = {};
        if (filters.languages?.length === 1) {
            filter.language = filters.languages[0];
        }
        if (filters.chunkTypes?.length === 1) {
            filter.type = filters.chunkTypes[0];
        }
        return Object.keys(filter).length > 0 ? filter : undefined;
    }
    /**
     * Check if chunk matches filter
     */
    matchesFilter(chunk, filter) {
        for (const [key, value] of Object.entries(filter)) {
            if (key === "language" && chunk.language !== value)
                return false;
            if (key === "type" && chunk.type !== value)
                return false;
            if (key === "filePath" && !chunk.filePath.includes(value))
                return false;
        }
        return true;
    }
    /**
     * Find text highlights
     */
    findHighlights(content, tokens) {
        const highlights = [];
        for (const token of tokens) {
            const regex = new RegExp(token, "gi");
            let match;
            while ((match = regex.exec(content)) !== null) {
                highlights.push({
                    start: match.index,
                    end: match.index + token.length,
                    text: match[0],
                });
            }
        }
        return highlights.slice(0, 10); // Limit highlights
    }
    /**
     * Remove chunks for a file
     */
    async removeFileChunks(filePath) {
        const chunkIds = this.fileIndex.get(filePath) || [];
        // Delete from chunk store (synchronous)
        for (const id of chunkIds) {
            this.chunkStore.delete(id);
        }
        // Delete from vector store in parallel for better performance
        await Promise.allSettled(chunkIds.map(id => this.vectorStore.delete(id)));
        this.fileIndex.delete(filePath);
    }
    /**
     * Find files to index
     */
    async findFiles(rootPath, options) {
        const files = [];
        const excludePatterns = options.excludePatterns || [];
        const walk = async (dir) => {
            const entries = await fsPromises.readdir(dir, { withFileTypes: true });
            for (const entry of entries) {
                const fullPath = path.join(dir, entry.name);
                const relativePath = path.relative(rootPath, fullPath);
                // Check exclusions
                if (this.matchesPatterns(relativePath, excludePatterns)) {
                    continue;
                }
                if (entry.isDirectory()) {
                    await walk(fullPath);
                }
                else if (entry.isFile()) {
                    // Check if it's a code file
                    const lang = detectLanguage(fullPath);
                    if (lang !== "text") {
                        files.push(fullPath);
                    }
                }
            }
        };
        await walk(rootPath);
        return files;
    }
    /**
     * Check if path matches any pattern
     */
    matchesPatterns(filePath, patterns) {
        for (const pattern of patterns) {
            if (this.matchesPattern(filePath, pattern)) {
                return true;
            }
        }
        return false;
    }
    /**
     * Simple glob pattern matching
     */
    matchesPattern(filePath, pattern) {
        // Convert glob to regex
        const regex = pattern
            .replace(/\*\*/g, "<<<DOUBLESTAR>>>")
            .replace(/\*/g, "[^/]*")
            .replace(/<<<DOUBLESTAR>>>/g, ".*")
            .replace(/\?/g, ".");
        return new RegExp(`^${regex}$`).test(filePath);
    }
    /**
     * Check if content appears to be binary
     */
    isBinaryContent(content) {
        // Check for null bytes
        if (content.includes("\0"))
            return true;
        // Check ratio of printable characters
        // eslint-disable-next-line no-control-regex
        const printable = content.replace(/[\x00-\x1f\x7f-\x9f]/g, "");
        return printable.length / content.length < 0.9;
    }
    /**
     * Tokenize text
     */
    tokenize(text) {
        return text
            .toLowerCase()
            .replace(/[^\w\s]/g, " ")
            .split(/\s+/)
            .filter(t => t.length > 1);
    }
    /**
     * Estimate token count
     */
    estimateTokens(text) {
        return Math.ceil(text.length / 4);
    }
    /**
     * Save index to disk
     */
    async saveIndex() {
        if (!this.config.indexPath)
            return;
        const dir = this.config.indexPath;
        const exists = await fsPromises.access(dir).then(() => true).catch(() => false);
        if (!exists) {
            await fsPromises.mkdir(dir, { recursive: true });
        }
        // Save chunks
        const chunks = Array.from(this.chunkStore.values()).map(chunk => ({
            ...chunk,
            embedding: undefined, // Don't save embeddings to chunk file
        }));
        await fsPromises.writeFile(path.join(dir, "chunks.json"), JSON.stringify(chunks), "utf-8");
        // Save file index
        await fsPromises.writeFile(path.join(dir, "file-index.json"), JSON.stringify(Object.fromEntries(this.fileIndex)), "utf-8");
        // Save stats
        await fsPromises.writeFile(path.join(dir, "stats.json"), JSON.stringify(this.indexStats), "utf-8");
        // Vector store saves itself
        if (this.vectorStore instanceof InMemoryVectorStore) {
            await this.vectorStore.saveToDisk();
        }
    }
    /**
     * Load index from disk
     */
    async loadIndex() {
        if (!this.config.indexPath)
            return false;
        const dir = this.config.indexPath;
        const dirExists = await fsPromises.access(dir).then(() => true).catch(() => false);
        if (!dirExists)
            return false;
        try {
            // Load chunks
            const chunksPath = path.join(dir, "chunks.json");
            const chunksExists = await fsPromises.access(chunksPath).then(() => true).catch(() => false);
            if (chunksExists) {
                const chunks = JSON.parse(await fsPromises.readFile(chunksPath, "utf-8"));
                for (const chunk of chunks) {
                    this.chunkStore.set(chunk.id, chunk);
                }
            }
            // Load file index
            const fileIndexPath = path.join(dir, "file-index.json");
            const fileIndexExists = await fsPromises.access(fileIndexPath).then(() => true).catch(() => false);
            if (fileIndexExists) {
                const fileIndex = JSON.parse(await fsPromises.readFile(fileIndexPath, "utf-8"));
                this.fileIndex = new Map(Object.entries(fileIndex));
            }
            // Load stats
            const statsPath = path.join(dir, "stats.json");
            const statsExists = await fsPromises.access(statsPath).then(() => true).catch(() => false);
            if (statsExists) {
                this.indexStats = JSON.parse(await fsPromises.readFile(statsPath, "utf-8"));
                this.indexStats.lastUpdated = new Date(this.indexStats.lastUpdated);
            }
            return true;
        }
        catch (error) {
            logger.warn("Failed to load index", { error });
            return false;
        }
    }
    /**
     * Get index statistics
     */
    getStats() {
        return { ...this.indexStats };
    }
    /**
     * Get a chunk by ID
     */
    getChunk(id) {
        return this.chunkStore.get(id);
    }
    /**
     * Get chunks for a file
     */
    getFileChunks(filePath) {
        const chunkIds = this.fileIndex.get(filePath) || [];
        return chunkIds
            .map(id => this.chunkStore.get(id))
            .filter((c) => c !== undefined);
    }
    /**
     * Clear the index
     */
    async clear() {
        this.chunkStore.clear();
        this.fileIndex.clear();
        await this.vectorStore.clear();
        this.indexStats = {
            totalChunks: 0,
            totalFiles: 0,
            totalTokens: 0,
            indexSize: 0,
            lastUpdated: new Date(),
            languages: {},
            chunkTypes: {},
        };
    }
    /**
     * Dispose resources
     */
    async dispose() {
        if (this.vectorStore instanceof InMemoryVectorStore) {
            await this.vectorStore.dispose();
        }
        this.chunkStore.clear();
        this.fileIndex.clear();
        this.removeAllListeners();
    }
}
/**
 * Create a CodebaseRAG instance
 */
export function createCodebaseRAG(config = {}) {
    return new CodebaseRAG(config);
}
// Singleton instance
let codebaseRAGInstance = null;
export function getCodebaseRAG(config = {}) {
    if (!codebaseRAGInstance) {
        codebaseRAGInstance = createCodebaseRAG(config);
    }
    return codebaseRAGInstance;
}
export function resetCodebaseRAG() {
    if (codebaseRAGInstance) {
        codebaseRAGInstance.dispose();
    }
    codebaseRAGInstance = null;
}
//# sourceMappingURL=codebase-rag.js.map