import assert from 'node:assert/strict';
import { mkdtemp, readdir, rm } from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { describe, it } from 'node:test';
import { MemoryCapacityError, MemoryStore } from './memory-store.js';
describe('MemoryStore', () => {
    it('creates and reads memory by uri', async () => {
        const dir = await mkdtemp(path.join(os.tmpdir(), 'aws-memory-store-'));
        const storePath = path.join(dir, 'memory.json');
        const store = new MemoryStore(storePath);
        try {
            const created = await store.createMemory({
                memory_type: 'memory',
                uri: 'core://agent/profile',
                content: 'I am a backend expert.'
            });
            assert.equal(created.uri, 'core://agent/profile');
            const loaded = await store.readMemory({ uri: 'core://agent/profile', memory_type: 'memory' });
            assert.ok(loaded);
            assert.equal(loaded?.content, 'I am a backend expert.');
        }
        finally {
            await rm(dir, { recursive: true, force: true });
        }
    });
    it('updates memory with exact replace and append', async () => {
        const dir = await mkdtemp(path.join(os.tmpdir(), 'aws-memory-store-'));
        const storePath = path.join(dir, 'memory.json');
        const store = new MemoryStore(storePath);
        try {
            await store.createMemory({
                memory_type: 'memory',
                uri: 'notes://project/todo',
                content: 'finish api docs'
            });
            const replaced = await store.updateMemory({
                memory_type: 'memory',
                uri: 'notes://project/todo',
                old_string: 'api docs',
                new_string: 'API docs v2'
            });
            assert.equal(replaced.content, 'finish API docs v2');
            const appended = await store.updateMemory({
                memory_type: 'memory',
                uri: 'notes://project/todo',
                append: '\n- add memory tests'
            });
            assert.match(appended.content, /add memory tests/);
        }
        finally {
            await rm(dir, { recursive: true, force: true });
        }
    });
    it('searches memories by query and supports limit', async () => {
        const dir = await mkdtemp(path.join(os.tmpdir(), 'aws-memory-store-'));
        const storePath = path.join(dir, 'memory.json');
        const store = new MemoryStore(storePath);
        try {
            await store.createMemory({
                memory_type: 'memory',
                uri: 'core://agent/skills',
                content: 'java spring boot mysql'
            });
            await store.createMemory({
                memory_type: 'memory',
                uri: 'notes://project/readme',
                content: 'project setup and architecture'
            });
            const result = await store.searchMemory({ query: 'project', limit: 1, memory_type: 'memory' });
            assert.equal(result.results.length, 1);
            assert.equal(result.results[0]?.uri, 'notes://project/readme');
        }
        finally {
            await rm(dir, { recursive: true, force: true });
        }
    });
    it('deletes memory and no longer reads it', async () => {
        const dir = await mkdtemp(path.join(os.tmpdir(), 'aws-memory-store-'));
        const storePath = path.join(dir, 'memory.json');
        const store = new MemoryStore(storePath);
        try {
            await store.createMemory({
                memory_type: 'memory',
                uri: 'core://agent/temp',
                content: 'temporary memory'
            });
            const deleted = await store.deleteMemory({ uri: 'core://agent/temp', memory_type: 'memory' });
            assert.equal(deleted, true);
            const loaded = await store.readMemory({ uri: 'core://agent/temp', memory_type: 'memory' });
            assert.equal(loaded, null);
        }
        finally {
            await rm(dir, { recursive: true, force: true });
        }
    });
    it('supports memory_type classification and list/filter', async () => {
        const dir = await mkdtemp(path.join(os.tmpdir(), 'aws-memory-store-'));
        const storePath = path.join(dir, 'memory.json');
        const store = new MemoryStore(storePath);
        try {
            const types = ['task_list', 'recently_memory', 'memory'];
            await store.createMemory({ memory_type: types[0], uri: 'task://todo/1', content: 'fix tests' });
            await store.createMemory({ memory_type: types[1], uri: 'recent://ctx/1', content: 'just discussed API design' });
            await store.createMemory({ memory_type: types[2], uri: 'core://archive/1', content: 'long-term architecture decision' });
            const taskList = await store.listByType('task_list');
            assert.equal(taskList.length, 1);
            assert.equal(taskList[0]?.memory_type, 'task_list');
            const recentList = await store.listByType('recently_memory');
            assert.equal(recentList.length, 1);
            assert.equal(recentList[0]?.memory_type, 'recently_memory');
            await assert.rejects(() => store.listByType('memory'), /Invalid memory_type/);
        }
        finally {
            await rm(dir, { recursive: true, force: true });
        }
    });
    it('removes task_list memory only via task_list_remove semantic', async () => {
        const dir = await mkdtemp(path.join(os.tmpdir(), 'aws-memory-store-'));
        const storePath = path.join(dir, 'memory.json');
        const store = new MemoryStore(storePath);
        try {
            await store.createMemory({ memory_type: 'task_list', uri: 'task://todo/remove-1', content: 'to be removed' });
            await store.createMemory({ memory_type: 'memory', uri: 'core://kept/1', content: 'should stay' });
            const removed = await store.removeTaskListMemory('task://todo/remove-1');
            assert.equal(removed, true);
            assert.equal(await store.readMemory({ uri: 'task://todo/remove-1' }), null);
            await assert.rejects(() => store.removeTaskListMemory('core://kept/1'), /task_list_remove/);
        }
        finally {
            await rm(dir, { recursive: true, force: true });
        }
    });
    it('archives recently_memory into memory', async () => {
        const dir = await mkdtemp(path.join(os.tmpdir(), 'aws-memory-store-'));
        const storePath = path.join(dir, 'memory.json');
        const store = new MemoryStore(storePath);
        try {
            await store.createMemory({ memory_type: 'recently_memory', uri: 'recent://note/1', content: 'hot memory' });
            const archived = await store.archiveRecentlyMemory('recent://note/1');
            assert.equal(archived.memory_type, 'memory');
            const loaded = await store.readMemory({ uri: 'recent://note/1', memory_type: 'memory' });
            assert.ok(loaded);
            assert.equal(loaded?.memory_type, 'memory');
            await store.createMemory({ memory_type: 'memory', uri: 'core://cold/2', content: 'cold memory' });
            await assert.rejects(() => store.archiveRecentlyMemory('core://cold/2'), /recently_memory_archive/);
        }
        finally {
            await rm(dir, { recursive: true, force: true });
        }
    });
    it('reports memory stats with listable and per-type limits', async () => {
        const dir = await mkdtemp(path.join(os.tmpdir(), 'aws-memory-store-'));
        const storePath = path.join(dir, 'memory.json');
        const store = new MemoryStore(storePath);
        try {
            await store.createMemory({ memory_type: 'task_list', uri: 'task://todo/stats', content: 'task' });
            await store.createMemory({ memory_type: 'recently_memory', uri: 'recent://ctx/stats', content: 'recent' });
            await store.createMemory({ memory_type: 'memory', uri: 'core://stats/long-term', content: 'long term' });
            const stats = await store.getStats();
            assert.equal(stats.total, 3);
            assert.deepEqual(stats.listable, { count: 2, limit: 64, hardLimit: 128, exceeded: false, hardExceeded: false });
            assert.deepEqual(stats.by_type.task_list, { count: 1, limit: 32, hardLimit: 64, exceeded: false, hardExceeded: false, listable: true });
            assert.deepEqual(stats.by_type.recently_memory, { count: 1, limit: 32, hardLimit: 64, exceeded: false, hardExceeded: false, listable: true });
            assert.deepEqual(stats.by_type.memory, { count: 1, limit: null, hardLimit: null, exceeded: false, hardExceeded: false, listable: false });
        }
        finally {
            await rm(dir, { recursive: true, force: true });
        }
    });
    it('saves memory data atomically without leaving temporary files after successful writes', async () => {
        const dir = await mkdtemp(path.join(os.tmpdir(), 'aws-memory-store-'));
        const storePath = path.join(dir, 'memory.json');
        const store = new MemoryStore(storePath);
        try {
            await store.createMemory({ memory_type: 'memory', uri: 'core://atomic/write', content: 'persisted' });
            const files = await readdir(dir);
            assert.deepEqual(files, ['memory.json']);
        }
        finally {
            await rm(dir, { recursive: true, force: true });
        }
    });
    it('allows listable memory beyond soft limits and rejects at the doubled hard type limit', async () => {
        const dir = await mkdtemp(path.join(os.tmpdir(), 'aws-memory-store-'));
        const storePath = path.join(dir, 'memory.json');
        const store = new MemoryStore(storePath);
        try {
            for (let index = 0; index < 64; index += 1) {
                await store.createMemory({
                    memory_type: 'recently_memory',
                    uri: `recent://ctx/${index}`,
                    content: `recent context ${index}`
                });
            }
            await assert.rejects(async () => {
                await store.createMemory({
                    memory_type: 'recently_memory',
                    uri: 'recent://ctx/overflow',
                    content: 'overflow context'
                });
            }, (error) => {
                assert.ok(error instanceof MemoryCapacityError);
                assert.equal(error.details.code, 'MEMORY_TYPE_LIMIT_REACHED');
                assert.equal(error.details.memory_type, 'recently_memory');
                assert.equal(error.details.limit, 64);
                assert.equal(error.details.current, 64);
                assert.ok(error.details.recommended_actions.includes('archive_recently_memory({ uri })'));
                assert.match(error.message, /hard type limit 64 reached.*delete an existing recently_memory.*archive one to long-term memory/s);
                return true;
            });
            await store.archiveRecentlyMemory('recent://ctx/0');
            const createdAfterArchive = await store.createMemory({
                memory_type: 'recently_memory',
                uri: 'recent://ctx/after-archive',
                content: 'capacity restored by archive'
            });
            assert.equal(createdAfterArchive.memory_type, 'recently_memory');
            const recentList = await store.listByType('recently_memory');
            assert.equal(recentList.length, 64);
        }
        finally {
            await rm(dir, { recursive: true, force: true });
        }
    });
    it('marks soft listable limits as exceeded without blocking creation', async () => {
        const dir = await mkdtemp(path.join(os.tmpdir(), 'aws-memory-store-'));
        const storePath = path.join(dir, 'memory.json');
        const store = new MemoryStore(storePath);
        try {
            for (let index = 0; index < 32; index += 1) {
                await store.createMemory({
                    memory_type: 'task_list',
                    uri: `task://todo/${index}`,
                    content: `task ${index}`
                });
                await store.createMemory({
                    memory_type: 'recently_memory',
                    uri: `recent://ctx/${index}`,
                    content: `recent context ${index}`
                });
            }
            const longTermMemory = await store.createMemory({
                memory_type: 'memory',
                uri: 'core://long-term/not-listable',
                content: 'long-term memories are not constrained by listable limits'
            });
            assert.equal(longTermMemory.memory_type, 'memory');
            const stats = await store.getStats();
            assert.equal(stats.listable.count, 64);
            assert.equal(stats.listable.exceeded, false);
            assert.equal(stats.listable.hardExceeded, false);
            assert.equal(stats.by_type.task_list.exceeded, false);
            assert.equal(stats.by_type.recently_memory.exceeded, false);
            const allowedBeyondSoftLimit = await store.createMemory({
                memory_type: 'task_list',
                uri: 'task://todo/beyond-soft-limit',
                content: 'allowed beyond soft limit until hard limit'
            });
            assert.equal(allowedBeyondSoftLimit.memory_type, 'task_list');
            const exceededStats = await store.getStats();
            assert.equal(exceededStats.listable.exceeded, true);
            assert.equal(exceededStats.by_type.task_list.exceeded, true);
        }
        finally {
            await rm(dir, { recursive: true, force: true });
        }
    });
    it('rejects invalid memory_type on create', async () => {
        const dir = await mkdtemp(path.join(os.tmpdir(), 'aws-memory-store-'));
        const storePath = path.join(dir, 'memory.json');
        const store = new MemoryStore(storePath);
        try {
            await assert.rejects(() => store.createMemory({ memory_type: 'invalid_type', uri: 'x://1', content: 'x' }), /Invalid memory_type/);
        }
        finally {
            await rm(dir, { recursive: true, force: true });
        }
    });
});
//# sourceMappingURL=memory-store.test.js.map