// storage/vector-store/sqlite-adapter.ts — SQLite storage backend using better-sqlite3.
// SPEC-075 AC-03: WAL mode, BLOB embeddings, optional native dependency.
import { cosineSimilarity } from '../../engine/vector-store/similarity.js';
import { projectDataDir } from '../base-store.js';
import { mkdirSync, existsSync } from 'node:fs';
import { dirname } from 'node:path';
/**
 * Attempt to dynamically import better-sqlite3.
 * Returns null if the package is not installed.
 */
export async function tryLoadSqlite() {
    try {
        // Dynamic import with variable to avoid TypeScript module resolution
        const moduleName = 'better-sqlite3';
        const mod = (await import(/* webpackIgnore: true */ moduleName));
        /* v8 ignore start -- better-sqlite3 not available in test env */
        return mod.default;
        /* v8 ignore stop */
    }
    catch {
        return null;
    }
}
/** Serialize a number[] embedding to a Buffer (Float32Array). */
function serializeEmbedding(embedding) {
    const float32 = new Float32Array(embedding);
    return Buffer.from(float32.buffer);
}
/** Deserialize a Buffer back to number[]. */
function deserializeEmbedding(buf) {
    const float32 = new Float32Array(buf.buffer, buf.byteOffset, buf.byteLength / 4);
    return [...float32];
}
/** Convert a database row to a VectorEntry. */
function rowToEntry(row) {
    return {
        id: row.id,
        type: row.type,
        content: row.content,
        embedding: deserializeEmbedding(row.embedding),
        metadata: JSON.parse(row.metadata),
        createdAt: row.createdAt,
        updatedAt: row.updatedAt,
    };
}
/**
 * SQLite storage backend using better-sqlite3.
 * WAL mode for concurrent reads. Embeddings stored as BLOB.
 */
export class SqliteBackend {
    db;
    constructor(db) {
        this.db = db;
        this.initSchema();
    }
    initSchema() {
        this.db.exec(`
      CREATE TABLE IF NOT EXISTS vectors (
        id TEXT PRIMARY KEY,
        type TEXT NOT NULL,
        content TEXT NOT NULL,
        embedding BLOB NOT NULL,
        metadata TEXT NOT NULL DEFAULT '{}',
        createdAt TEXT NOT NULL,
        updatedAt TEXT NOT NULL
      );
      CREATE INDEX IF NOT EXISTS idx_vectors_type ON vectors(type);
    `);
    }
    async insert(entry) {
        this.db
            .prepare(`INSERT OR REPLACE INTO vectors (id, type, content, embedding, metadata, createdAt, updatedAt)
         VALUES (?, ?, ?, ?, ?, ?, ?)`)
            .run(entry.id, entry.type, entry.content, serializeEmbedding(entry.embedding), JSON.stringify(entry.metadata), entry.createdAt, entry.updatedAt);
        await Promise.resolve();
    }
    async update(id, partial) {
        const existing = await this.getById(id);
        if (!existing) {
            return;
        }
        const merged = { ...existing, ...partial, id: existing.id };
        await this.insert(merged);
    }
    async delete(id) {
        this.db.prepare('DELETE FROM vectors WHERE id = ?').run(id);
        await Promise.resolve();
    }
    async getById(id) {
        const row = this.db.prepare('SELECT * FROM vectors WHERE id = ?').get(id);
        await Promise.resolve();
        return row ? rowToEntry(row) : undefined;
    }
    async getAll(type) {
        let rows;
        if (type) {
            rows = this.db.prepare('SELECT * FROM vectors WHERE type = ?').all(type);
        }
        else {
            rows = this.db.prepare('SELECT * FROM vectors').all();
        }
        await Promise.resolve();
        return rows.map(rowToEntry);
    }
    async searchSimilar(embedding, topK, threshold, type) {
        // Load all entries of the given type and compute similarity in JS.
        const entries = await this.getAll(type);
        const results = [];
        for (const entry of entries) {
            const similarity = cosineSimilarity(embedding, entry.embedding);
            if (similarity >= threshold) {
                results.push({ entry, similarity });
            }
        }
        results.sort((a, b) => b.similarity - a.similarity);
        return results.slice(0, topK);
    }
    close() {
        this.db.close();
    }
}
/**
 * Create a SQLite backend for a project.
 * Returns null if better-sqlite3 is not available.
 */
export async function createSqliteBackend(projectId) {
    const SqliteConstructor = await tryLoadSqlite();
    /* v8 ignore start -- entire path after this only reachable with better-sqlite3 */
    if (!SqliteConstructor) {
        return null;
    }
    const dbPath = `${projectDataDir(projectId)}/vector-store.db`;
    const dir = dirname(dbPath);
    if (!existsSync(dir)) {
        mkdirSync(dir, { recursive: true });
    }
    const db = SqliteConstructor(dbPath);
    db.pragma('journal_mode = WAL');
    return new SqliteBackend(db);
    /* v8 ignore stop */
}
//# sourceMappingURL=sqlite-adapter.js.map