import { readFile } from "node:fs/promises";
import { LogFormatter } from "@zuplo/core/cli";
import chalk from "chalk";
const logFormatter = new LogFormatter(async (path) => {
    return readFile(path, { encoding: "utf8" });
});
export function createRuntimeStdioHandler(options) {
    return function handleRuntimeStdio(stdout, stderr) {
        const { prefix, prefixColor } = options ?? {};
        let stdoutAccumulator = "";
        let stderrAccumulator = "";
        const classifiers = {
            isBarf(chunk) {
                const containsLlvmSymbolizerWarning = chunk.includes("Not symbolizing stack traces because $LLVM_SYMBOLIZER is not set");
                const containsRecursiveIsolateLockWarning = chunk.includes("took recursive isolate lock");
                const containsHexStack = /stack:( (0|[a-f\d]{4,})){3,}/.test(chunk);
                return (containsLlvmSymbolizerWarning ||
                    containsRecursiveIsolateLockWarning ||
                    containsHexStack);
            },
            isAddressInUse(chunk) {
                return chunk.includes("Address already in use; toString() = ");
            },
            isWarning(chunk) {
                return /\.c\+\+:\d+: warning:/.test(chunk);
            },
            isCodeMovedWarning(chunk) {
                return /CODE_MOVED for unknown code block/.test(chunk);
            },
            isAccessViolation(chunk) {
                return chunk.includes("access violation;");
            },
            isZenoLog(chunk) {
                return chunk.startsWith("[ZENO]:");
            },
        };
        stdout.on("data", (chunk) => {
            const fullStreamOutput = `${stdoutAccumulator}${chunk}`;
            let currentLogsStr = "";
            const lastNewlineIdx = fullStreamOutput.lastIndexOf("\n");
            if (lastNewlineIdx > 0) {
                currentLogsStr = fullStreamOutput.slice(0, lastNewlineIdx);
                stdoutAccumulator = fullStreamOutput.slice(lastNewlineIdx + 1);
            }
            else {
                stdoutAccumulator = fullStreamOutput;
                return;
            }
            const lines = currentLogsStr.split("\n");
            for (let line of lines) {
                line = line.trim();
                if (!line)
                    continue;
                if (classifiers.isBarf(line)) {
                    logConsole("debug", line, prefix, prefixColor);
                }
                else if (classifiers.isWarning(line)) {
                    logConsole("warn", line, prefix, prefixColor);
                }
                else if (classifiers.isZenoLog(line)) {
                    const levels = ["debug", "info", "warn", "error"];
                    const level = levels.find((level) => line.includes(`[${level.toUpperCase()}]`));
                    line = line.substring("[ZENO]:".length);
                    line = line.substring(`[${level?.toUpperCase()}]`.length);
                    if (level) {
                        logConsole(level, line, prefix, prefixColor);
                    }
                    else {
                        logConsole("info", line, prefix, prefixColor);
                    }
                }
                else {
                    logConsole("error", line, prefix, prefixColor);
                }
            }
        });
        stderr.on("data", (chunk) => {
            const fullStreamOutput = `${stderrAccumulator}${chunk}`;
            let currentLogsStr = "";
            const lastNewlineIdx = fullStreamOutput.lastIndexOf("\n");
            if (lastNewlineIdx > 0) {
                currentLogsStr = fullStreamOutput.slice(0, lastNewlineIdx);
                stderrAccumulator = fullStreamOutput.slice(lastNewlineIdx + 1);
            }
            else {
                stderrAccumulator = fullStreamOutput;
                return;
            }
            const lines = currentLogsStr.split("\n");
            for (let line of lines) {
                line = line.trim();
                if (!line)
                    continue;
                if (classifiers.isBarf(line)) {
                    if (classifiers.isAddressInUse(line)) {
                        const address = line.match(/Address already in use; toString\(\) = (.+)\n/)?.[1];
                        logConsole("error", `Address already in use (${address}). Please check that you are not already running a server on this address or specify a different port with --port.`, prefix, prefixColor);
                        logConsole("debug", line, prefix, prefixColor);
                    }
                    else if (classifiers.isAccessViolation(line)) {
                        let error = "There was an access violation in the runtime.";
                        if (process.platform === "win32") {
                            error +=
                                "\nOn Windows, this may be caused by an outdated Microsoft Visual C++ Redistributable library.\n" +
                                    "Check that you have the latest version installed.\n" +
                                    "See https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist.";
                        }
                        logConsole("error", error, prefix, prefixColor);
                        logConsole("debug", line, prefix, prefixColor);
                    }
                    else {
                        logConsole("debug", line, prefix, prefixColor);
                    }
                }
                else if (classifiers.isWarning(line)) {
                    logConsole("warn", line, prefix, prefixColor);
                }
                else if (classifiers.isCodeMovedWarning(line)) {
                }
                else if (classifiers.isZenoLog(line)) {
                    line = line.substring("[ZENO]:".length);
                    line = line.substring("[ERROR]".length);
                    logConsole("error", line, prefix, prefixColor);
                }
                else {
                    logConsole("error", line, prefix, prefixColor);
                }
            }
        });
    };
}
function logConsole(level, data, prefix, prefixColor) {
    void logConsoleAsync(level, data, prefix, prefixColor);
}
async function logConsoleAsync(level, data, prefix, prefixColor) {
    let outputPrefix = "";
    if (prefix && prefixColor) {
        const paddedName = prefix.padEnd(10);
        outputPrefix = prefixColor(paddedName) + chalk.gray(" | ");
    }
    try {
        if (!data) {
            return;
        }
        if (typeof data !== "string") {
            console[level](outputPrefix + data);
            return;
        }
        let message = data?.trim();
        if (!message || message?.length === 0) {
            return;
        }
        if (message.startsWith(`{`) && message.endsWith(`}`)) {
            let logEntry;
            try {
                logEntry = JSON.parse(message);
            }
            catch {
                console[level](outputPrefix + data);
                return;
            }
            await Promise.all(logEntry.messages.map(async (msg) => {
                const line = await logFormatter.formatStructured(logEntry, msg);
                console[level](outputPrefix + line);
            }));
        }
        else {
            message = await logFormatter.formatMessage(message);
            const line = logFormatter.formatLine(level, message);
            console[level](outputPrefix + line);
        }
    }
    catch {
        const line = logFormatter.formatLine(level, data);
        console[level](outputPrefix + line);
    }
}
//# sourceMappingURL=worker-output.js.map