/**
 * FTS5 Search Strategy
 *
 * Implements the KnowledgeSearchStrategy interface using the SQLite FTS5
 * full-text search index. Maps FTS5 BM25-ranked results to the existing
 * ScoredDocument format used by KnowledgeSearchService.
 *
 * @module services/knowledge/fts5-search-strategy
 */
import { LoggerService } from '../core/logger.service.js';
/**
 * FTS5-based search strategy for the Wiki-First Knowledge System.
 *
 * This strategy provides pure full-text search via SQLite FTS5 with BM25 ranking.
 *
 * Note on semantic fallback (design doc Section 10): The 10% semantic fallback
 * weight is achieved at the MemoryService level, which runs both FTS5 knowledge
 * search AND vector-based semantic search in parallel. This strategy intentionally
 * does not mix semantic results — that composition happens upstream in the recall
 * pipeline (MemoryService.recall -> searchKnowledgeDocuments + semanticSearch).
 *
 * Uses SQLite FTS5 MATCH with BM25 ranking for fast, zero-cost retrieval.
 * Results are mapped back to the KnowledgeDocumentSummary objects provided
 * by the caller, falling back to minimal summaries when a document is found
 * in the FTS5 index but not in the provided documents array.
 *
 * @example
 * ```typescript
 * const strategy = new Fts5SearchStrategy(wikiCompiler);
 * const results = await strategy.search('deployment guide', documents);
 * ```
 */
export class Fts5SearchStrategy {
    wikiCompiler;
    logger;
    /**
     * Create an Fts5SearchStrategy.
     *
     * @param wikiCompiler - The WikiCompilerService instance providing FTS5 search
     */
    constructor(wikiCompiler) {
        this.wikiCompiler = wikiCompiler;
        this.logger = LoggerService.getInstance().createComponentLogger('Fts5SearchStrategy');
    }
    /**
     * Search for documents matching the query using FTS5 full-text search.
     *
     * FTS5 BM25 rank values are negative (more negative = more relevant),
     * so scores are converted to positive values for the ScoredDocument interface.
     *
     * @param query - The search query text
     * @param documents - Candidate document summaries to match against
     * @returns Documents sorted by descending relevance score
     */
    async search(query, documents) {
        const ftsResults = this.wikiCompiler.search(query, { limit: 10 });
        if (ftsResults.length === 0) {
            this.logger.debug('FTS5 search returned no results', { query });
            return [];
        }
        // Build a lookup map from the provided documents array
        const docById = new Map(documents.map((d) => [d.id, d]));
        const scored = [];
        for (const ftsResult of ftsResults) {
            // FTS5 rank is negative; convert to positive score (higher = more relevant)
            const score = Math.abs(ftsResult.rank);
            const existingDoc = docById.get(ftsResult.id);
            if (existingDoc) {
                scored.push({ document: existingDoc, score });
            }
            else {
                // Create a minimal summary for documents in FTS5 but not in the provided list
                const minimalDoc = {
                    id: ftsResult.id,
                    title: ftsResult.title,
                    category: ftsResult.category,
                    tags: ftsResult.tags ? ftsResult.tags.split(/,\s*/) : [],
                    preview: ftsResult.content.slice(0, 200),
                    scope: 'global',
                    createdBy: 'system',
                    updatedBy: 'system',
                    createdAt: new Date().toISOString(),
                    updatedAt: new Date().toISOString(),
                };
                scored.push({ document: minimalDoc, score });
            }
        }
        return scored.sort((a, b) => b.score - a.score);
    }
}
//# sourceMappingURL=fts5-search-strategy.js.map