"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.searchFileIndex = searchFileIndex;
const embedder_1 = require("../../lib/embedder");
const defaults_1 = require("../../lib/defaults");
const bm25_1 = require("../../lib/search/bm25");
/**
 * Hybrid search over file nodes by path embedding + BM25 keyword matching.
 * Only searches file nodes (directories have empty embeddings).
 * No BFS expansion.
 */
function searchFileIndex(graph, queryEmbedding, options = {}) {
    const { topK = defaults_1.FILE_SEARCH_TOP_K, minScore = defaults_1.SEARCH_MIN_SCORE_FILES, queryText, bm25Index } = options;
    // Vector scoring
    const vectorScores = new Map();
    if (queryEmbedding.length > 0) {
        graph.forEachNode((id, attrs) => {
            if (attrs.kind !== 'file' || attrs.embedding.length === 0)
                return;
            vectorScores.set(id, (0, embedder_1.cosineSimilarity)(queryEmbedding, attrs.embedding));
        });
    }
    // BM25 scoring
    const bm25Scores = queryText && bm25Index ? bm25Index.score(queryText) : new Map();
    // Fuse or use single source
    let scored;
    if (vectorScores.size > 0 && bm25Scores.size > 0) {
        const fused = (0, bm25_1.rrfFuse)(vectorScores, bm25Scores, defaults_1.RRF_K);
        scored = [...fused.entries()].map(([id, score]) => ({ id, score }));
        const maxScore = scored.reduce((m, s) => Math.max(m, s.score), 0);
        if (maxScore > 0)
            for (const s of scored)
                s.score /= maxScore;
    }
    else if (bm25Scores.size > 0) {
        scored = [...bm25Scores.entries()].map(([id, score]) => ({ id, score }));
        const maxScore = scored.reduce((m, s) => Math.max(m, s.score), 0);
        if (maxScore > 0)
            for (const s of scored)
                s.score /= maxScore;
    }
    else {
        scored = [...vectorScores.entries()].map(([id, score]) => ({ id, score }));
    }
    return scored
        .filter(s => s.score >= minScore)
        .sort((a, b) => b.score - a.score)
        .slice(0, topK)
        .map(s => {
        const attrs = graph.getNodeAttributes(s.id);
        return {
            filePath: attrs.filePath,
            fileName: attrs.fileName,
            extension: attrs.extension,
            language: attrs.language,
            size: attrs.size,
            score: s.score,
        };
    });
}
