const DEFAULT_LIMIT = 10;
const MAX_LIMIT = 50;
const MAX_CONTENT_LENGTH = 2_000;
function normalizeLimit(limit) {
    if (typeof limit !== "number" || !Number.isFinite(limit) || limit <= 0) {
        return DEFAULT_LIMIT;
    }
    return Math.min(Math.floor(limit), MAX_LIMIT);
}
export function buildFtsQuery(query) {
    const terms = query
        .toLowerCase()
        .match(/[a-z0-9_]+/g)
        ?.map((term) => term.trim())
        .filter((term) => term.length >= 2)
        .slice(0, 12) ?? [];
    if (terms.length === 0)
        return null;
    return [...new Set(terms)]
        .map((term) => `"${term.replace(/"/g, '""')}"`)
        .join(" OR ");
}
function cleanSnippet(snippet, fallback) {
    return (snippet?.replace(/\s+/g, " ").trim() || fallback.slice(0, 240)).trim();
}
function truncateContent(content) {
    const normalized = content.replace(/\s+/g, " ").trim();
    if (normalized.length <= MAX_CONTENT_LENGTH)
        return normalized;
    return `${normalized.slice(0, MAX_CONTENT_LENGTH - 3)}...`;
}
function mergeActivityContent(summary, payload) {
    if (!payload)
        return summary;
    return `${summary}\n${payload}`;
}
export class SessionSearchService {
    sqlite;
    constructor(sqlite) {
        this.sqlite = sqlite;
    }
    search(options) {
        const ftsQuery = buildFtsQuery(options.query);
        if (!ftsQuery)
            return [];
        const limit = normalizeLimit(options.limit);
        const includeMessages = options.includeMessages !== false;
        const includeThreadActivities = options.includeThreadActivities !== false;
        const rows = [];
        if (includeMessages) {
            rows.push(...this.searchMessages(ftsQuery, options, limit));
        }
        if (includeThreadActivities) {
            rows.push(...this.searchThreadActivities(ftsQuery, options, limit));
        }
        return rows
            .sort((a, b) => b.score - a.score || b.createdAt.localeCompare(a.createdAt))
            .slice(0, limit);
    }
    searchMessages(ftsQuery, options, limit) {
        const userId = options.userId ?? null;
        const sessionId = options.sessionId ?? null;
        const rows = this.sqlite.prepare(`
      SELECT
        m.id AS id,
        m.session_id AS sessionId,
        s.name AS sessionName,
        m.role AS role,
        m.content AS content,
        snippet(messages_fts, 0, '[', ']', '...', 24) AS snippet,
        m.created_at AS createdAt,
        (0 - bm25(messages_fts)) AS score
      FROM messages_fts
      JOIN messages m ON m.id = messages_fts.message_id
      JOIN sessions s ON s.id = m.session_id
      WHERE messages_fts MATCH ?
        AND (? IS NULL OR s.user_id = ?)
        AND (? IS NULL OR m.session_id = ?)
      ORDER BY bm25(messages_fts), m.created_at DESC
      LIMIT ?
    `).all(ftsQuery, userId, userId, sessionId, sessionId, limit);
        return rows.map((row) => ({
            source: "message",
            id: row.id,
            sessionId: row.sessionId,
            sessionName: row.sessionName,
            role: row.role,
            content: truncateContent(row.content),
            snippet: cleanSnippet(row.snippet, row.content),
            createdAt: row.createdAt,
            score: row.score,
        }));
    }
    searchThreadActivities(ftsQuery, options, limit) {
        const userId = options.userId ?? null;
        const sessionId = options.sessionId ?? null;
        const threadId = options.threadId ?? null;
        const rows = this.sqlite.prepare(`
      SELECT
        a.id AS id,
        t.session_id AS sessionId,
        a.thread_id AS threadId,
        t.title AS threadTitle,
        a.kind AS kind,
        a.summary AS summary,
        a.payload AS payload,
        snippet(agent_thread_activities_fts, 0, '[', ']', '...', 24) AS snippet,
        a.created_at AS createdAt,
        (0 - bm25(agent_thread_activities_fts)) AS score
      FROM agent_thread_activities_fts
      JOIN agent_thread_activities a ON a.id = agent_thread_activities_fts.activity_id
      JOIN agent_threads t ON t.id = a.thread_id
      WHERE agent_thread_activities_fts MATCH ?
        AND (? IS NULL OR t.user_id = ?)
        AND (? IS NULL OR t.session_id = ?)
        AND (? IS NULL OR a.thread_id = ?)
      ORDER BY bm25(agent_thread_activities_fts), a.created_at DESC
      LIMIT ?
    `).all(ftsQuery, userId, userId, sessionId, sessionId, threadId, threadId, limit);
        return rows.map((row) => {
            const content = mergeActivityContent(row.summary, row.payload);
            return {
                source: "thread_activity",
                id: row.id,
                sessionId: row.sessionId,
                threadId: row.threadId,
                threadTitle: row.threadTitle,
                kind: row.kind,
                content: truncateContent(content),
                snippet: cleanSnippet(row.snippet, content),
                createdAt: row.createdAt,
                score: row.score,
            };
        });
    }
}
//# sourceMappingURL=session-search.js.map