import fs from 'node:fs';
import path from 'node:path';
import { gunzipSync, gzipSync } from 'node:zlib';
import { messages } from '../core/messages/index.js';
const PADRAO_MAX_OCORRENCIAS = 5000;
const PADRAO_MAX_BYTES = 10 * 1024 * 1024;
const { StreamingRelatorioMensagens } = messages;
export class RelatorioStream {
    manifest;
    options;
    finalized = false;
    buffer = [];
    currentShardIndex = 0;
    currentShardOcorrencias = 0;
    currentShardBytes = 0;
    currentShardFile = '';
    firstTimestamp = '';
    constructor(options) {
        const opts = {
            outputDir: options.outputDir,
            prefix: options.prefix ?? 'tutanus-stream',
            maxOcorrenciasPerShard: options.maxOcorrenciasPerShard ?? PADRAO_MAX_OCORRENCIAS,
            maxBytesPerShard: options.maxBytesPerShard ?? PADRAO_MAX_BYTES,
            compress: options.compress ?? true,
            format: options.format ?? 'ndjson',
        };
        fs.mkdirSync(opts.outputDir, { recursive: true });
        const now = new Date().toISOString();
        this.manifest = {
            version: 1,
            generatedAt: now,
            format: opts.format,
            totalOcorrencias: 0,
            totalShards: 0,
            compressed: opts.compress,
            shards: [],
        };
        this.options = opts;
        this.currentShardFile = this.gerarCaminhoShard(opts, 0);
        this.firstTimestamp = now;
    }
    gerarCaminhoShard(opts, index) {
        const ext = opts.format === 'markdown' ? 'md' : 'ndjson';
        const suffix = opts.compress ? '.gz' : '';
        const nome = `${opts.prefix}-parte-${String(index + 1).padStart(4, '0')}.${ext}${suffix}`;
        return path.join(opts.outputDir, nome);
    }
    write(ocorrencia) {
        if (this.finalized)
            return;
        this.manifest.totalOcorrencias++;
        this.currentShardOcorrencias++;
        this.buffer.push(ocorrencia);
        if (this.buffer.length >= 100) {
            this.flushBuffer();
        }
        if (this.currentShardOcorrencias >= this.options.maxOcorrenciasPerShard ||
            this.currentShardBytes >= this.options.maxBytesPerShard) {
            this.rotacionarShard();
        }
    }
    writeBatch(ocorrencias) {
        for (const ocorrencia of ocorrencias) {
            this.write(ocorrencia);
        }
    }
    flushBuffer() {
        if (this.buffer.length === 0 || this.finalized)
            return;
        let dados = '';
        for (const ocorrencia of this.buffer) {
            dados += `${JSON.stringify(ocorrencia)}\n`;
        }
        const buf = Buffer.from(dados, 'utf-8');
        if (this.options.compress) {
            const existing = fs.existsSync(this.currentShardFile) ? fs.readFileSync(this.currentShardFile) : Buffer.alloc(0);
            const decompressed = existing.length > 0 ? descomprimirGzip(existing) : Buffer.alloc(0);
            const combined = Buffer.concat([decompressed, buf]);
            fs.writeFileSync(this.currentShardFile, gzipSync(combined));
        }
        else {
            fs.appendFileSync(this.currentShardFile, buf);
        }
        this.currentShardBytes += buf.length;
        this.buffer = [];
    }
    rotacionarShard() {
        this.flushBuffer();
        const stats = fs.existsSync(this.currentShardFile) ? fs.statSync(this.currentShardFile) : null;
        this.manifest.shards.push({
            index: this.currentShardIndex,
            file: path.basename(this.currentShardFile),
            ocorrencias: this.currentShardOcorrencias,
            bytes: stats?.size ?? 0,
            firstTimestamp: this.firstTimestamp,
            lastTimestamp: new Date().toISOString(),
        });
        this.manifest.totalShards++;
        this.currentShardIndex++;
        this.currentShardOcorrencias = 0;
        this.currentShardBytes = 0;
        this.currentShardFile = this.gerarCaminhoShard(this.options, this.currentShardIndex);
        this.firstTimestamp = new Date().toISOString();
    }
    getManifest() {
        return { ...this.manifest };
    }
    async end(metadata) {
        if (this.finalized)
            throw new Error(StreamingRelatorioMensagens.jaFinalizado);
        this.flushBuffer();
        this.finalized = true;
        if (this.currentShardOcorrencias > 0 || this.manifest.shards.length === 0) {
            const stats = fs.existsSync(this.currentShardFile) ? fs.statSync(this.currentShardFile) : null;
            this.manifest.shards.push({
                index: this.currentShardIndex,
                file: path.basename(this.currentShardFile),
                ocorrencias: this.currentShardOcorrencias,
                bytes: stats?.size ?? 0,
                firstTimestamp: this.firstTimestamp,
                lastTimestamp: new Date().toISOString(),
            });
            this.manifest.totalShards++;
        }
        if (metadata) {
            this.manifest.metadata = metadata;
        }
        const manifestFile = `${this.options.prefix}-manifest.json`;
        const manifestPath = path.join(this.options.outputDir, manifestFile);
        fs.writeFileSync(manifestPath, JSON.stringify(this.manifest, null, 2), 'utf-8');
        return {
            manifestFile,
            manifest: this.manifest,
        };
    }
    getTotalOcorrencias() {
        return this.manifest.totalOcorrencias;
    }
    getShardCount() {
        return Math.max(this.manifest.totalShards, this.currentShardIndex + 1);
    }
}
function descomprimirGzip(buf) {
    try {
        return gunzipSync(buf);
    }
    catch {
        return buf;
    }
}
export async function criarStreamParaProjeto(outputDir, options) {
    return new RelatorioStream({
        outputDir,
        ...options,
    });
}
//# sourceMappingURL=streaming-report.js.map