/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import ansiEscapes from 'ansi-escapes';
const ESC = '\u001B[';
const ERASE_LINE = `${ESC}2K`;
const CURSOR_UP_ONE = `${ESC}1A`;
const CURSOR_DOWN_ONE = `${ESC}1B`;
const CURSOR_LEFT = `${ESC}G`;
const MULTILINE_ERASE_LINES_PATTERN = new RegExp(`(?:${escapeRegExp(ERASE_LINE + CURSOR_UP_ONE)})+${escapeRegExp(ERASE_LINE + CURSOR_LEFT)}`, 'g');
function escapeRegExp(value) {
    return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
function countOccurrences(value, search) {
    let count = 0;
    let index = 0;
    while ((index = value.indexOf(search, index)) !== -1) {
        count++;
        index += search.length;
    }
    return count;
}
const terminalRedrawStats = {
    stdoutWriteCount: 0,
    stdoutBytes: 0,
    clearTerminalCount: 0,
    eraseLinesOptimizedCount: 0,
};
export function getTerminalRedrawStatsSnapshot() {
    return { ...terminalRedrawStats };
}
export function resetTerminalRedrawStats() {
    terminalRedrawStats.stdoutWriteCount = 0;
    terminalRedrawStats.stdoutBytes = 0;
    terminalRedrawStats.clearTerminalCount = 0;
    terminalRedrawStats.eraseLinesOptimizedCount = 0;
}
function getChunkByteLength(chunk, encodingOrCallback) {
    if (typeof chunk === 'string') {
        const encoding = typeof encodingOrCallback === 'string' ? encodingOrCallback : undefined;
        return Buffer.byteLength(chunk, encoding);
    }
    return chunk.byteLength;
}
function optimizeMultilineEraseLinesWithCount(output) {
    let optimizedSequenceCount = 0;
    const optimizedOutput = output.replace(MULTILINE_ERASE_LINES_PATTERN, (sequence) => {
        const lineCount = countOccurrences(sequence, ERASE_LINE);
        const cursorUpCount = lineCount - 1;
        if (cursorUpCount <= 1) {
            return sequence;
        }
        optimizedSequenceCount += 1;
        let boundedErase = `${ESC}${cursorUpCount}A`;
        for (let line = 0; line < lineCount; line++) {
            boundedErase += ERASE_LINE;
            if (line < lineCount - 1) {
                boundedErase += CURSOR_DOWN_ONE;
            }
        }
        return `${boundedErase}${ESC}${cursorUpCount}A${CURSOR_LEFT}`;
    });
    return { output: optimizedOutput, optimizedSequenceCount };
}
/**
 * Ink clears dynamic output via ansi-escapes.eraseLines(), which emits a
 * clear-line + cursor-up pair for every previous line. That can make terminal
 * scrollback bounce during frequent streaming renders. Collapse the repeated
 * upward cursor movement while still clearing only the same old frame lines.
 */
export function optimizeMultilineEraseLines(output) {
    return optimizeMultilineEraseLinesWithCount(output).output;
}
export function installTerminalRedrawOptimizer(stdout) {
    if (process.env['HOPCODE_LEGACY_ERASE_LINES'] === '1') {
        return () => { };
    }
    const originalWrite = stdout.write;
    const optimizedWrite = function (chunk, encodingOrCallback, callback) {
        const optimizedResult = typeof chunk === 'string'
            ? optimizeMultilineEraseLinesWithCount(chunk)
            : undefined;
        const optimizedChunk = optimizedResult?.output ?? chunk;
        if (typeof optimizedChunk === 'string' ||
            optimizedChunk instanceof Uint8Array ||
            Buffer.isBuffer(optimizedChunk)) {
            terminalRedrawStats.stdoutWriteCount += 1;
            terminalRedrawStats.stdoutBytes += getChunkByteLength(optimizedChunk, encodingOrCallback);
            if (typeof optimizedChunk === 'string') {
                terminalRedrawStats.clearTerminalCount += countOccurrences(optimizedChunk, ansiEscapes.clearTerminal);
            }
        }
        if (optimizedResult) {
            terminalRedrawStats.eraseLinesOptimizedCount +=
                optimizedResult.optimizedSequenceCount;
        }
        return originalWrite.call(this, optimizedChunk, encodingOrCallback, callback);
    };
    stdout.write = optimizedWrite;
    return () => {
        if (stdout.write === optimizedWrite) {
            stdout.write = originalWrite;
        }
    };
}
//# sourceMappingURL=terminalRedrawOptimizer.js.map