import { writeRecord } from '../backends/perfile_source.js';
/** Idempotently add the two compression columns to an existing `lessons` table. */
export async function ensureCompressionColumns(client) {
    for (const ddl of [
        `ALTER TABLE lessons ADD COLUMN derived_from TEXT NOT NULL DEFAULT '[]'`,
        `ALTER TABLE lessons ADD COLUMN consumed_by_user_lessons INTEGER NOT NULL DEFAULT 0`,
        // Scope columns (T-memory-scope-isolation) — additive, idempotent, same as the compression cols.
        `ALTER TABLE lessons ADD COLUMN tier TEXT NOT NULL DEFAULT 'shared'`,
        `ALTER TABLE lessons ADD COLUMN namespace TEXT`,
    ]) {
        try {
            await client.execute(ddl);
        }
        catch {
            // duplicate-column = already migrated; any other error surfaces on the SELECT/INSERT below.
        }
    }
}
const str = (v) => (typeof v === 'string' ? v : '');
const num = (v) => typeof v === 'number' ? v : typeof v === 'bigint' ? Number(v) : 0;
function rowToMemory(row) {
    return {
        id: str(row.id),
        content: str(row.content),
        tags: JSON.parse(str(row.tags) || '[]'),
        source: str(row.source),
        author: str(row.author) === 'user' ? 'user' : 'agent',
        createdAt: str(row.created_at),
        derivedFrom: JSON.parse(str(row.derived_from) || '[]'),
        consumedByUserLessons: num(row.consumed_by_user_lessons),
        tier: str(row.tier) === 'project' ? 'project' : 'shared',
        namespace: typeof row.namespace === 'string' ? row.namespace : null,
    };
}
/** Load one memory by id (the `lessons` row), or null. */
export async function getMemoryById(client, id) {
    const rs = await client.execute({
        sql: `SELECT id, content, tags, source, author, created_at, derived_from, consumed_by_user_lessons, tier, namespace
          FROM lessons WHERE id = ?`,
        args: [id],
    });
    if (rs.rows.length === 0)
        return null;
    return rowToMemory(rs.rows[0]);
}
/**
 * Insert a memory carrying the compression columns (+ FTS + optional vector). Idempotent upsert by id.
 * File-first when `sourceDir` is given: writes the per-file source-of-truth (atomic) BEFORE the DB
 * upsert, mirroring `storeLesson` — so a consolidated memory survives a `rebuildLibsqlIndex` (which
 * re-indexes from files only). `sourceDir` undefined → DB-only (the pre-existing behavior).
 */
export async function insertMemory(client, m, sourceDir) {
    if (sourceDir !== undefined)
        await writeRecord(sourceDir, m);
    const tagsJson = JSON.stringify(m.tags);
    const vec = m.embedding ?? null;
    await client.execute({ sql: `DELETE FROM lessons WHERE id = ?`, args: [m.id] });
    await client.execute({ sql: `DELETE FROM lessons_fts WHERE id = ?`, args: [m.id] });
    const tier = m.tier ?? 'shared';
    const namespace = m.namespace ?? null;
    await client.execute({
        sql: `INSERT INTO lessons (id, content, tags, source, author, created_at, derived_from,
          consumed_by_user_lessons, tier, namespace, embedding)
          VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ${vec ? 'vector32(?)' : 'NULL'})`,
        args: vec
            ? [
                m.id,
                m.content,
                tagsJson,
                m.source,
                m.author,
                m.createdAt,
                JSON.stringify(m.derivedFrom),
                m.consumedByUserLessons,
                tier,
                namespace,
                JSON.stringify(vec),
            ]
            : [
                m.id,
                m.content,
                tagsJson,
                m.source,
                m.author,
                m.createdAt,
                JSON.stringify(m.derivedFrom),
                m.consumedByUserLessons,
                tier,
                namespace,
            ],
    });
    await client.execute({
        sql: `INSERT INTO lessons_fts (id, content, tags, source) VALUES (?, ?, ?, ?)`,
        args: [m.id, m.content, tagsJson, m.source],
    });
}
/** The auto-memory import marker, as a tag (libSQL has no `origin` column): `origin:import:<name>`. */
export const IMPORT_TAG_PREFIX = 'origin:import:';
/** Paged list of memory rows in a deterministic order, each carrying its tags (the import marker). */
export async function listMemories(client, opts) {
    const totalRs = await client.execute(`SELECT COUNT(*) AS n FROM lessons`);
    const total = num(totalRs.rows[0]?.n);
    const rs = await client.execute({
        sql: `SELECT id, content, tags, source, author, created_at, derived_from, consumed_by_user_lessons
          FROM lessons ORDER BY created_at, id LIMIT ? OFFSET ?`,
        args: [opts.limit, opts.offset],
    });
    const results = rs.rows.map((r) => rowToMemory(r));
    return { results, returned: results.length, total };
}
/**
 * Update a memory's content (the folded `description\n\nbody`) + its `scope:` tag, re-embedding via
 * `embed`. Preserves every OTHER tag (incl. the `origin:import:` marker) + derived_from/counters by
 * loading the row and reusing the file-first `insertMemory` upsert. Throws if the id is absent.
 */
export async function updateMemory(client, embed, opts) {
    const row = await getMemoryById(client, opts.id);
    if (row === null)
        throw new Error(`updateMemory: ${opts.id} not found`);
    const tags = row.tags.filter((t) => !t.startsWith('scope:')); // replace, don't append
    if (opts.scope !== undefined)
        tags.push(`scope:${opts.scope}`);
    const embedding = await embed(opts.content);
    await insertMemory(client, { ...row, content: opts.content, tags, embedding });
}
//# sourceMappingURL=store.js.map