import { mkdir, readFile, rename, unlink, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import { generateMemoryId } from '../../utils/id.js';
import { getRootLogger } from '../../utils/logger.js';
import { InMemoryMemoryIndex } from './index.js';
export class DiskMemoryStore {
    baseDir;
    log;
    index = new InMemoryMemoryIndex();
    initialized = false;
    constructor(config) {
        this.baseDir = join(config.baseDir, 'memory');
        this.log = (config.logger ?? getRootLogger()).child({ component: 'DiskMemoryStore' });
    }
    get indexPath() {
        return join(this.baseDir, 'index.json');
    }
    get contentDir() {
        return join(this.baseDir, 'content');
    }
    contentPath(id) {
        return join(this.contentDir, `${id}.json`);
    }
    async ensureInit() {
        if (this.initialized)
            return;
        await mkdir(this.contentDir, { recursive: true });
        try {
            const raw = await readFile(this.indexPath, 'utf-8');
            const entries = JSON.parse(raw);
            this.index.rebuild(entries);
            this.log.info('Memory index loaded', { count: entries.length });
        }
        catch (err) {
            const isNotFound = typeof err === 'object' && err !== null && err.code === 'ENOENT';
            if (!isNotFound) {
                this.log.warn('Failed to read memory index — starting fresh', {
                    error: String(err),
                });
            }
        }
        this.initialized = true;
    }
    async create(params) {
        await this.ensureInit();
        const id = generateMemoryId();
        const now = Date.now();
        const entry = {
            id,
            title: params.title,
            summary: params.summary,
            tags: params.tags ? [...params.tags] : [],
            status: 'active',
            createdAt: now,
            updatedAt: now,
        };
        const memoryContent = {
            id,
            content: params.content,
            format: params.format ?? 'text',
            metadata: params.metadata ? { ...params.metadata } : undefined,
        };
        this.index.set(entry);
        await this.persistIndex();
        await atomicWriteJson(this.contentPath(id), memoryContent);
        this.log.info('Memory created', { memoryId: id, title: params.title });
        return { entry, content: memoryContent };
    }
    async get(id) {
        await this.ensureInit();
        if (!this.index.getEntry(id))
            return undefined;
        try {
            const raw = await readFile(this.contentPath(id), 'utf-8');
            return JSON.parse(raw);
        }
        catch {
            this.log.warn('Failed to read memory content', { memoryId: id });
            return undefined;
        }
    }
    async update(id, updates) {
        await this.ensureInit();
        const existing = this.index.getEntry(id);
        if (!existing)
            return undefined;
        const now = Date.now();
        const updated = {
            ...existing,
            title: updates.title ?? existing.title,
            summary: updates.summary ?? existing.summary,
            tags: updates.tags ? [...updates.tags] : existing.tags,
            updatedAt: now,
        };
        this.index.set(updated);
        await this.persistIndex();
        if (updates.content !== undefined ||
            updates.format !== undefined ||
            updates.metadata !== undefined) {
            try {
                const raw = await readFile(this.contentPath(id), 'utf-8');
                const existingContent = JSON.parse(raw);
                const updatedContent = {
                    ...existingContent,
                    content: updates.content ?? existingContent.content,
                    format: updates.format ?? existingContent.format,
                    metadata: updates.metadata !== undefined ? { ...updates.metadata } : existingContent.metadata,
                };
                await atomicWriteJson(this.contentPath(id), updatedContent);
            }
            catch {
                this.log.warn('Failed to update memory content', { memoryId: id });
            }
        }
        this.log.info('Memory updated', { memoryId: id });
        return updated;
    }
    async delete(id) {
        await this.ensureInit();
        const existed = this.index.remove(id);
        if (!existed)
            return false;
        await this.persistIndex();
        await unlink(this.contentPath(id)).catch(() => undefined);
        this.log.info('Memory deleted', { memoryId: id });
        return true;
    }
    async list(params) {
        await this.ensureInit();
        return this.index.search(params ?? {});
    }
    getIndex() {
        return this.index;
    }
    async persistIndex() {
        const entries = this.index.allEntries();
        await atomicWriteJson(this.indexPath, entries);
    }
}
async function atomicWriteFile(filePath, content) {
    const tempPath = `${filePath}.tmp`;
    try {
        await writeFile(tempPath, content, 'utf-8');
        await rename(tempPath, filePath);
    }
    catch (err) {
        await unlink(tempPath).catch(() => undefined);
        throw err;
    }
}
async function atomicWriteJson(filePath, value) {
    await atomicWriteFile(filePath, JSON.stringify(value, null, 2));
}
//# sourceMappingURL=disk.js.map