import { appendFile, mkdir, readFile, readdir, rename, unlink, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import { getRootLogger } from '../../utils/logger.js';
export class RunDiskStore {
    baseDir;
    runDir = null;
    log;
    indexLock = Promise.resolve();
    constructor(config) {
        this.baseDir = config.baseDir;
        this.log = (config.logger ?? getRootLogger()).child({ component: 'RunDiskStore' });
    }
    requireInit() {
        if (!this.runDir) {
            throw new Error('RunDiskStore not initialized — call initRun() first');
        }
        return this.runDir;
    }
    async initRun(runId, parentRunId) {
        if (parentRunId) {
            this.runDir = join(this.baseDir, parentRunId, 'children', runId);
        }
        else {
            this.runDir = join(this.baseDir, runId);
        }
        await mkdir(this.runDir, { recursive: true });
        this.log.info(`Run directory created: ${this.runDir}`);
        return this.runDir;
    }
    async appendEvent(event) {
        const dir = this.requireInit();
        const line = `${JSON.stringify({
            ...event,
            timestamp: Date.now(),
        })}\n`;
        await appendFile(join(dir, 'transcript.jsonl'), line, 'utf-8');
    }
    async writeRunMeta(run) {
        const dir = this.requireInit();
        const meta = {
            id: run.id,
            status: run.status,
            metadata: run.metadata,
            tokenUsage: run.tokenUsage,
            currentIteration: run.currentIteration,
            startedAt: run.startedAt,
            endedAt: run.endedAt,
            lastError: run.lastError,
            messageCount: run.messages.length,
        };
        if (run.parentRunId)
            meta.parentRunId = run.parentRunId;
        if (run.depth !== undefined && run.depth > 0)
            meta.depth = run.depth;
        await atomicWriteJson(join(dir, 'run.json'), meta);
    }
    async writeMessages(run) {
        const dir = this.requireInit();
        await atomicWriteJson(join(dir, 'messages.json'), run.messages);
    }
    async writeReport(content) {
        const dir = this.requireInit();
        const reportPath = join(dir, 'report.md');
        await atomicWriteFile(reportPath, content);
        this.log.info(`Report written: ${reportPath}`);
        return reportPath;
    }
    getRunDir() {
        return this.runDir;
    }
    async writeCheckpoint(checkpoint) {
        const dir = this.requireInit();
        const cpDir = join(dir, 'checkpoints');
        await mkdir(cpDir, { recursive: true });
        await atomicWriteJson(join(cpDir, `${checkpoint.id}.json`), checkpoint);
    }
    async readCheckpoint(checkpointId) {
        const dir = this.requireInit();
        try {
            const content = await readFile(join(dir, 'checkpoints', `${checkpointId}.json`), 'utf-8');
            return JSON.parse(content);
        }
        catch (err) {
            if (isFileNotFound(err))
                return null;
            throw err;
        }
    }
    async listCheckpoints() {
        const dir = this.requireInit();
        const cpDir = join(dir, 'checkpoints');
        try {
            const files = await readdir(cpDir);
            const checkpoints = [];
            for (const file of files) {
                if (!file.endsWith('.json'))
                    continue;
                try {
                    const content = await readFile(join(cpDir, file), 'utf-8');
                    checkpoints.push(JSON.parse(content));
                }
                catch {
                    this.log.warn(`Failed to parse checkpoint file: ${file}`);
                }
            }
            return checkpoints.sort((a, b) => a.createdAt - b.createdAt);
        }
        catch (err) {
            if (isFileNotFound(err))
                return [];
            throw err;
        }
    }
    async deleteCheckpoint(checkpointId) {
        const dir = this.requireInit();
        try {
            await unlink(join(dir, 'checkpoints', `${checkpointId}.json`));
        }
        catch (err) {
            if (!isFileNotFound(err))
                throw err;
        }
    }
    static async listRuns(baseDir) {
        try {
            const indexPath = join(baseDir, 'index.json');
            const content = await readFile(indexPath, 'utf-8');
            return JSON.parse(content);
        }
        catch (err) {
            if (isFileNotFound(err))
                return [];
            throw err;
        }
    }
    async addToIndex(run) {
        if (run.parentRunId)
            return;
        const prev = this.indexLock;
        let resolve;
        this.indexLock = new Promise((r) => {
            resolve = r;
        });
        try {
            await prev;
            const indexPath = join(this.baseDir, 'index.json');
            let index = [];
            try {
                const content = await readFile(indexPath, 'utf-8');
                index = JSON.parse(content);
            }
            catch (err) {
                if (!isFileNotFound(err))
                    throw err;
            }
            const entry = {
                id: run.id,
                agentId: run.metadata.agentId,
                agentName: run.metadata.agentName,
                model: run.metadata.config.model,
                status: run.status,
                startedAt: run.startedAt,
                endedAt: run.endedAt,
                iterations: run.currentIteration,
                totalTokens: run.tokenUsage.totalTokens,
            };
            const existingIdx = index.findIndex((e) => e.id === run.id);
            if (existingIdx >= 0) {
                index[existingIdx] = entry;
            }
            else {
                index.push(entry);
            }
            await atomicWriteJson(indexPath, index);
        }
        finally {
            resolve?.();
        }
    }
}
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));
}
function isFileNotFound(err) {
    return typeof err === 'object' && err !== null && err.code === 'ENOENT';
}
//# sourceMappingURL=disk.js.map