/**
 * Memory Repository
 *
 * Repository for persistent memory storage with vector embeddings.
 */
import { getDatabaseManager } from '../database-manager.js';
// ============================================================================
// Memory Repository
// ============================================================================
export class MemoryRepository {
    db;
    constructor(db) {
        this.db = db || getDatabaseManager().getDatabase();
    }
    /**
     * Create a new memory
     */
    create(memory) {
        const stmt = this.db.prepare(`
      INSERT INTO memories (id, type, scope, project_id, content, embedding, importance, metadata, expires_at)
      VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
      ON CONFLICT(scope, project_id, content) DO UPDATE SET
        importance = MAX(memories.importance, excluded.importance),
        access_count = memories.access_count + 1,
        last_accessed = CURRENT_TIMESTAMP
      RETURNING *
    `);
        const embeddingBlob = memory.embedding
            ? Buffer.from(memory.embedding.buffer)
            : null;
        const result = stmt.get(memory.id, memory.type, memory.scope, memory.project_id || null, memory.content, embeddingBlob, memory.importance, memory.metadata ? JSON.stringify(memory.metadata) : null, memory.expires_at || null);
        return this.deserializeMemory(result);
    }
    /**
     * Get memory by ID
     */
    getById(id) {
        const stmt = this.db.prepare('SELECT * FROM memories WHERE id = ?');
        const result = stmt.get(id);
        if (!result)
            return null;
        // Update access stats
        this.db.prepare(`
      UPDATE memories SET access_count = access_count + 1, last_accessed = CURRENT_TIMESTAMP WHERE id = ?
    `).run(id);
        return this.deserializeMemory(result);
    }
    /**
     * Find memories by filter
     */
    find(filter = {}) {
        let sql = 'SELECT * FROM memories WHERE 1=1';
        const params = [];
        if (filter.type) {
            if (Array.isArray(filter.type)) {
                sql += ` AND type IN (${filter.type.map(() => '?').join(',')})`;
                params.push(...filter.type);
            }
            else {
                sql += ' AND type = ?';
                params.push(filter.type);
            }
        }
        if (filter.scope) {
            sql += ' AND scope = ?';
            params.push(filter.scope);
        }
        if (filter.projectId) {
            sql += ' AND project_id = ?';
            params.push(filter.projectId);
        }
        if (filter.minImportance !== undefined) {
            sql += ' AND importance >= ?';
            params.push(filter.minImportance);
        }
        // Exclude expired memories
        sql += ' AND (expires_at IS NULL OR expires_at > CURRENT_TIMESTAMP)';
        sql += ' ORDER BY importance DESC, last_accessed DESC';
        if (filter.limit) {
            sql += ' LIMIT ?';
            params.push(filter.limit);
        }
        if (filter.offset) {
            sql += ' OFFSET ?';
            params.push(filter.offset);
        }
        const stmt = this.db.prepare(sql);
        const results = stmt.all(...params);
        return results.map(r => this.deserializeMemory(r));
    }
    /**
     * Search memories by semantic similarity
     */
    searchSimilar(embedding, filter = {}, topK = 10) {
        // Get all candidate memories
        const candidates = this.find({ ...filter, limit: 100 });
        // Calculate cosine similarity for each
        const results = [];
        for (const memory of candidates) {
            if (!memory.embedding)
                continue;
            const similarity = this.cosineSimilarity(embedding, memory.embedding);
            results.push({ memory, similarity });
        }
        // Sort by similarity and return top K
        return results
            .sort((a, b) => b.similarity - a.similarity)
            .slice(0, topK);
    }
    /**
     * Update memory
     */
    update(id, updates) {
        const fields = [];
        const params = [];
        if (updates.type !== undefined) {
            fields.push('type = ?');
            params.push(updates.type);
        }
        if (updates.scope !== undefined) {
            fields.push('scope = ?');
            params.push(updates.scope);
        }
        if (updates.project_id !== undefined) {
            fields.push('project_id = ?');
            params.push(updates.project_id);
        }
        if (updates.content !== undefined) {
            fields.push('content = ?');
            params.push(updates.content);
        }
        if (updates.embedding !== undefined) {
            fields.push('embedding = ?');
            params.push(Buffer.from(updates.embedding.buffer));
        }
        if (updates.importance !== undefined) {
            fields.push('importance = ?');
            params.push(updates.importance);
        }
        if (updates.metadata !== undefined) {
            fields.push('metadata = ?');
            params.push(JSON.stringify(updates.metadata));
        }
        if (updates.expires_at !== undefined) {
            fields.push('expires_at = ?');
            params.push(updates.expires_at);
        }
        if (fields.length === 0)
            return false;
        params.push(id);
        const stmt = this.db.prepare(`UPDATE memories SET ${fields.join(', ')} WHERE id = ?`);
        const result = stmt.run(...params);
        return result.changes > 0;
    }
    /**
     * Delete memory
     */
    delete(id) {
        const stmt = this.db.prepare('DELETE FROM memories WHERE id = ?');
        const result = stmt.run(id);
        return result.changes > 0;
    }
    /**
     * Delete expired memories
     */
    deleteExpired() {
        const stmt = this.db.prepare('DELETE FROM memories WHERE expires_at IS NOT NULL AND expires_at <= CURRENT_TIMESTAMP');
        const result = stmt.run();
        return result.changes;
    }
    /**
     * Get memory statistics
     */
    getStats() {
        const total = this.db.prepare('SELECT COUNT(*) as count FROM memories').get().count;
        const byTypeRows = this.db.prepare('SELECT type, COUNT(*) as count FROM memories GROUP BY type').all();
        const byType = {};
        for (const row of byTypeRows) {
            byType[row.type] = row.count;
        }
        const byScopeRows = this.db.prepare('SELECT scope, COUNT(*) as count FROM memories GROUP BY scope').all();
        const byScope = {};
        for (const row of byScopeRows) {
            byScope[row.scope] = row.count;
        }
        const avgRow = this.db.prepare('SELECT AVG(importance) as avg FROM memories').get();
        const avgImportance = avgRow.avg || 0;
        return { total, byType, byScope, avgImportance };
    }
    /**
     * Bulk insert memories
     */
    bulkCreate(memories) {
        const stmt = this.db.prepare(`
      INSERT OR IGNORE INTO memories (id, type, scope, project_id, content, embedding, importance, metadata, expires_at)
      VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
    `);
        const insertMany = this.db.transaction((items) => {
            let count = 0;
            for (const memory of items) {
                const embeddingBlob = memory.embedding
                    ? Buffer.from(memory.embedding.buffer)
                    : null;
                const result = stmt.run(memory.id, memory.type, memory.scope, memory.project_id || null, memory.content, embeddingBlob, memory.importance, memory.metadata ? JSON.stringify(memory.metadata) : null, memory.expires_at || null);
                if (result.changes > 0)
                    count++;
            }
            return count;
        });
        return insertMany(memories);
    }
    // ============================================================================
    // Private Methods
    // ============================================================================
    deserializeMemory(row) {
        let embedding;
        if (row.embedding) {
            const buf = row.embedding;
            embedding = new Float32Array(buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength));
        }
        return {
            ...row,
            embedding,
            metadata: row.metadata ? JSON.parse(row.metadata) : undefined,
        };
    }
    cosineSimilarity(a, b) {
        if (a.length !== b.length)
            return 0;
        let dotProduct = 0;
        let normA = 0;
        let normB = 0;
        for (let i = 0; i < a.length; i++) {
            dotProduct += a[i] * b[i];
            normA += a[i] * a[i];
            normB += b[i] * b[i];
        }
        const magnitude = Math.sqrt(normA) * Math.sqrt(normB);
        return magnitude === 0 ? 0 : dotProduct / magnitude;
    }
}
// ============================================================================
// Singleton Instance
// ============================================================================
let instance = null;
export function getMemoryRepository() {
    if (!instance) {
        instance = new MemoryRepository();
    }
    return instance;
}
export function resetMemoryRepository() {
    instance = null;
}
//# sourceMappingURL=memory-repository.js.map