"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.rebuildFtsIndex = rebuildFtsIndex;
exports.isFtsStale = isFtsStale;
exports.fuzzySearchMemory = fuzzySearchMemory;
exports.getSearchSuggestions = getSearchSuggestions;
exports.getFtsHealth = getFtsHealth;
const database_1 = require("../database/database");
// ============================================================
// Proactive FTS rebuild — call after bulk operations or in doctor
// ============================================================
function rebuildFtsIndex() {
    const db = (0, database_1.getDb)();
    try {
        const eventCount = db.prepare('SELECT COUNT(*) as c FROM memory_events').get().c;
        db.exec('DELETE FROM memory_events_fts');
        db.exec('INSERT INTO memory_events_fts(rowid, summary, topic, details_json, tags_json, files_json) SELECT id, summary, topic, details_json, tags_json, files_json FROM memory_events');
        const ftsCount = db.prepare('SELECT COUNT(*) as c FROM memory_events_fts').get().c;
        const ratio = eventCount > 0 ? Math.round(ftsCount / eventCount * 100) : 100;
        return { rebuilt: true, eventCount, ftsCount, message: `FTS rebuilt: ${ftsCount}/${eventCount} events (${ratio}%)` };
    }
    catch (e) {
        return { rebuilt: false, eventCount: 0, ftsCount: 0, message: `FTS rebuild failed: ${String(e)}` };
    }
}
function isFtsStale() {
    const db = (0, database_1.getDb)();
    try {
        const eventCount = db.prepare('SELECT COUNT(*) as c FROM memory_events').get().c;
        let ftsCount = 0;
        try {
            ftsCount = db.prepare('SELECT COUNT(*) as c FROM memory_events_fts').get().c;
        }
        catch { /* FTS might not exist */ }
        const ratio = eventCount > 0 ? Math.round(ftsCount / eventCount * 100) : 100;
        return { stale: ratio < 80, eventCount, ftsCount, ratio };
    }
    catch {
        return { stale: false, eventCount: 0, ftsCount: 0, ratio: 100 };
    }
}
function fuzzySearchMemory(options) {
    const db = (0, database_1.getDb)();
    const { query, project, type, minImportance, maxImportance, topic, days, limit = 20, sortBy = 'relevance' } = options;
    let useFts = false;
    try {
        const ftsRow = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='memory_events_fts'").get();
        useFts = !!ftsRow;
    }
    catch { /* FTS not available */ }
    if (useFts && query) {
        try {
            const ftsCount = db.prepare("SELECT COUNT(*) as c FROM memory_events_fts").get();
            const eventCount = db.prepare("SELECT COUNT(*) as c FROM memory_events").get();
            // Lazy rebuild if FTS is stale
            if (eventCount.c > 0 && ftsCount.c < eventCount.c * 0.8) {
                try {
                    db.exec("DELETE FROM memory_events_fts");
                    db.exec("INSERT INTO memory_events_fts(rowid, summary, topic, details_json, tags_json, files_json) SELECT id, summary, topic, details_json, tags_json, files_json FROM memory_events");
                }
                catch { /* rebuild failed, use LIKE */ }
            }
            // Re-read after rebuild
            const ftsCountAfter = db.prepare("SELECT COUNT(*) as c FROM memory_events_fts").get();
            if (ftsCountAfter.c > 0) {
                const ftsResults = searchWithFts(db, options);
                if (ftsResults.length > 0)
                    return ftsResults;
                // FTS returned 0 results — fall through to LIKE
            }
        }
        catch { /* fallback to LIKE */ }
    }
    let sql = 'SELECT * FROM memory_events WHERE 1=1';
    const params = [];
    if (project) {
        sql += ' AND project = ?';
        params.push(project);
    }
    if (type) {
        sql += ' AND type = ?';
        params.push(type);
    }
    if (minImportance !== undefined) {
        sql += ' AND importance >= ?';
        params.push(minImportance);
    }
    if (maxImportance !== undefined) {
        sql += ' AND importance <= ?';
        params.push(maxImportance);
    }
    if (topic) {
        sql += ' AND topic = ?';
        params.push(topic);
    }
    if (days) {
        const since = new Date(Date.now() - days * 86400000).toISOString();
        sql += ' AND created_at >= ?';
        params.push(since);
    }
    if (query) {
        const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
        const conditions = terms.map(() => '(LOWER(summary) LIKE ? OR LOWER(details_json) LIKE ? OR LOWER(topic) LIKE ? OR LOWER(files_json) LIKE ?)');
        sql += ` AND (${conditions.join(' OR ')})`;
        for (const term of terms) {
            const like = `%${term}%`;
            params.push(like, like, like, like);
        }
    }
    switch (sortBy) {
        case 'importance':
            sql += ' ORDER BY importance DESC, created_at DESC';
            break;
        case 'recent':
            sql += ' ORDER BY created_at DESC';
            break;
        case 'relevance':
        default:
            if (query) {
                sql += ' ORDER BY importance DESC, created_at DESC';
            }
            else {
                sql += ' ORDER BY created_at DESC';
            }
    }
    sql += ` LIMIT ?`;
    params.push(limit);
    const rows = db.prepare(sql).all(...params);
    if (query && sortBy === 'relevance') {
        const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
        for (const row of rows) {
            row.score = calcRelevanceScore(row, terms);
        }
        rows.sort((a, b) => b.score - a.score);
    }
    return rows;
}
function searchWithFts(db, options) {
    const { query, project, type, minImportance, maxImportance, topic, days, limit = 20, sortBy = 'relevance' } = options;
    const ftsQuery = (query || '').split(/\s+/).filter(Boolean).map(t => `"${t.replace(/"/g, '""')}"*`).join(' OR ');
    if (!ftsQuery)
        return [];
    // Standalone FTS5: we need to join manually by rowid
    let sql = `
    SELECT me.*
    FROM memory_events_fts fts
    JOIN memory_events me ON me.rowid = fts.rowid
    WHERE memory_events_fts MATCH ?
  `;
    const params = [ftsQuery];
    if (project) {
        sql += ' AND me.project = ?';
        params.push(project);
    }
    if (type) {
        sql += ' AND me.type = ?';
        params.push(type);
    }
    if (minImportance !== undefined) {
        sql += ' AND me.importance >= ?';
        params.push(minImportance);
    }
    if (maxImportance !== undefined) {
        sql += ' AND me.importance <= ?';
        params.push(maxImportance);
    }
    if (topic) {
        sql += ' AND me.topic = ?';
        params.push(topic);
    }
    if (days) {
        const since = new Date(Date.now() - days * 86400000).toISOString();
        sql += ' AND me.created_at >= ?';
        params.push(since);
    }
    switch (sortBy) {
        case 'importance':
            sql += ' ORDER BY me.importance DESC, me.created_at DESC';
            break;
        case 'recent':
            sql += ' ORDER BY me.created_at DESC';
            break;
        case 'relevance':
        default:
            sql += ' ORDER BY me.importance DESC, me.created_at DESC';
    }
    sql += ' LIMIT ?';
    params.push(limit);
    const rows = db.prepare(sql).all(...params);
    for (const row of rows) {
        row.score = row.score || 0;
    }
    return rows;
}
function calcRelevanceScore(row, terms) {
    let score = 0;
    const summaryLower = (row.summary || '').toLowerCase();
    const detailsLower = (row.details_json || '').toLowerCase();
    const topicLower = (row.topic || '').toLowerCase();
    const filesLower = (row.files_json || '').toLowerCase();
    for (const term of terms) {
        // Exact match in summary = high score
        if (summaryLower.includes(term))
            score += 30;
        // Match in topic
        if (topicLower.includes(term))
            score += 25;
        // Match in details
        if (detailsLower.includes(term))
            score += 20;
        // Match in files
        if (filesLower.includes(term))
            score += 15;
    }
    // Importance boost
    score += row.importance * 3;
    // Recency boost (last 24h = +20, last 7d = +10)
    const ageHours = (Date.now() - new Date(row.created_at).getTime()) / 3600000;
    if (ageHours < 24)
        score += 20;
    else if (ageHours < 168)
        score += 10;
    // Type boost for important types
    const typeBoost = { decision: 15, error: 12, fix: 14, pattern: 13, product_rule: 16 };
    score += typeBoost[row.type] || 0;
    return score;
}
function getSearchSuggestions(project, partial) {
    const db = (0, database_1.getDb)();
    const like = `%${partial.toLowerCase()}%`;
    const rows = db.prepare(`
    SELECT DISTINCT topic FROM memory_events
    WHERE project = ? AND topic IS NOT NULL AND LOWER(topic) LIKE ?
    ORDER BY topic LIMIT 10
  `).all(project, like);
    const summaryRows = db.prepare(`
    SELECT DISTINCT summary FROM memory_events
    WHERE project = ? AND LOWER(summary) LIKE ?
    ORDER BY created_at DESC LIMIT 5
  `).all(project, like);
    const suggestions = [
        ...rows.map(r => r.topic),
        ...summaryRows.map(r => r.summary.slice(0, 60)),
    ];
    return [...new Set(suggestions)].slice(0, 10);
}
function getFtsHealth() {
    const db = (0, database_1.getDb)();
    // Check if FTS table exists
    let ftsExists = false;
    try {
        const ftsRow = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='memory_events_fts'").get();
        ftsExists = !!ftsRow;
    }
    catch { /* FTS not available */ }
    if (!ftsExists) {
        return {
            ftsExists: false,
            eventCount: 0,
            ftsCount: 0,
            ratio: 100,
            status: 'missing',
            message: 'FTS table does not exist. Run rebuildFtsIndex() to create.',
        };
    }
    // Get counts
    let eventCount = 0;
    let ftsCount = 0;
    try {
        eventCount = db.prepare('SELECT COUNT(*) as c FROM memory_events').get().c;
        ftsCount = db.prepare('SELECT COUNT(*) as c FROM memory_events_fts').get().c;
    }
    catch { /* ignore */ }
    const ratio = eventCount > 0 ? Math.round(ftsCount / eventCount * 100) : 100;
    let status;
    let message;
    if (eventCount === 0 && ftsCount === 0) {
        status = 'empty';
        message = 'No events indexed. FTS is ready but empty.';
    }
    else if (ratio >= 80) {
        status = 'healthy';
        message = `FTS healthy: ${ftsCount}/${eventCount} events indexed (${ratio}%).`;
    }
    else if (ratio >= 50) {
        status = 'degraded';
        message = `FTS degraded: ${ftsCount}/${eventCount} events indexed (${ratio}%). Lazy rebuild will fix on next query.`;
    }
    else {
        status = 'degraded';
        message = `FTS severely degraded: ${ftsCount}/${eventCount} events indexed (${ratio}%). Run rebuildFtsIndex() to fix.`;
    }
    return { ftsExists, eventCount, ftsCount, ratio, status, message };
}
//# sourceMappingURL=memory-search.js.map