import { createReadStream, createWriteStream } from 'node:fs';
import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
import { once } from 'node:events';
import path from 'node:path';
import readline from 'node:readline';
import { CODEBASE_INDEX_SCHEMA_VERSION } from './types.js';
import { abortIfNeeded } from '../utils.js';
function metaPath(directory) {
    return path.join(directory, 'meta.json');
}
function filesPath(directory) {
    return path.join(directory, 'files.ndjson');
}
function nodesPath(directory) {
    return path.join(directory, 'nodes.ndjson');
}
function edgesPath(directory) {
    return path.join(directory, 'edges.ndjson');
}
export async function writeNdjsonFile(filePath, entries, signal) {
    const stream = createWriteStream(filePath, { encoding: 'utf8' });
    try {
        for (const entry of entries) {
            abortIfNeeded(signal);
            if (!stream.write(`${JSON.stringify(entry)}\n`)) {
                await once(stream, 'drain');
            }
        }
        stream.end();
        await once(stream, 'finish');
    }
    finally {
        stream.destroy();
    }
}
export async function readNdjsonFile(filePath, signal) {
    const entries = [];
    const stream = createReadStream(filePath, { encoding: 'utf8' });
    const reader = readline.createInterface({
        input: stream,
        crlfDelay: Infinity,
    });
    try {
        for await (const line of reader) {
            abortIfNeeded(signal);
            if (!line)
                continue;
            entries.push(JSON.parse(line));
        }
    }
    finally {
        reader.close();
        stream.destroy();
    }
    return entries;
}
export async function writeSectionedCodeIndex(directory, index, extraMeta = {}, signal) {
    await rm(directory, { recursive: true, force: true });
    await mkdir(directory, { recursive: true });
    abortIfNeeded(signal);
    const meta = {
        repoRoot: index.repoRoot,
        contentMode: index.contentMode,
        capabilities: index.capabilities,
        parserRefusals: index.parserRefusals,
        skippedFiles: index.skippedFiles,
        fileCount: index.files.length,
        nodeCount: index.nodes.length,
        edgeCount: index.edges.length,
        truncated: index.truncated,
        ...extraMeta,
        indexSchemaVersion: index.schemaVersion ?? CODEBASE_INDEX_SCHEMA_VERSION,
    };
    await writeFile(metaPath(directory), JSON.stringify(meta, null, 2), 'utf8');
    abortIfNeeded(signal);
    // `lines` is derivable from `content` (a single split), so persisting both
    // doubles the on-disk payload for full-mode files. Drop the derived array
    // when content is present; readSectionedCodeIndex rebuilds it on load.
    const filesToWrite = index.files.map(file => file.content != null && file.lines != null ? { ...file, lines: undefined } : file);
    await writeNdjsonFile(filesPath(directory), filesToWrite, signal);
    abortIfNeeded(signal);
    await writeNdjsonFile(nodesPath(directory), index.nodes, signal);
    abortIfNeeded(signal);
    await writeNdjsonFile(edgesPath(directory), index.edges, signal);
}
export async function readSectionedCodeIndex(directory, signal) {
    abortIfNeeded(signal);
    const rawMeta = await readFile(metaPath(directory), 'utf8');
    const meta = JSON.parse(rawMeta);
    if (meta.indexSchemaVersion !== undefined && meta.indexSchemaVersion !== CODEBASE_INDEX_SCHEMA_VERSION) {
        throw new Error(`Unsupported code index schema version ${String(meta.indexSchemaVersion)}; expected ${CODEBASE_INDEX_SCHEMA_VERSION}.`);
    }
    const rawFiles = await readNdjsonFile(filesPath(directory), signal);
    // Rebuild `lines` from `content` for full-mode files (writeSectionedCodeIndex
    // omits the derived array). Caches that predate that change still carry
    // `lines`, so only rebuild when it is absent.
    const files = rawFiles.map(file => file.content != null && file.lines == null ? { ...file, lines: file.content.split(/\r?\n/) } : file);
    const nodes = await readNdjsonFile(nodesPath(directory), signal);
    const edges = await readNdjsonFile(edgesPath(directory), signal);
    return {
        meta,
        index: {
            schemaVersion: meta.indexSchemaVersion ?? CODEBASE_INDEX_SCHEMA_VERSION,
            repoRoot: meta.repoRoot,
            contentMode: meta.contentMode ?? 'excerpt',
            capabilities: meta.capabilities ?? {},
            parserRefusals: meta.parserRefusals,
            skippedFiles: meta.skippedFiles ?? [],
            truncated: meta.truncated,
            files,
            nodes,
            edges,
        },
    };
}
//# sourceMappingURL=index-io.js.map