"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RuntimeSfgeWrapper = void 0;
const node_path_1 = __importDefault(require("node:path"));
const node_fs_1 = __importDefault(require("node:fs"));
const code_analyzer_engine_api_1 = require("@salesforce/code-analyzer-engine-api");
const messages_1 = require("./messages");
const utils_1 = require("./utils");
const SFGE_MAIN_JAVA_CLASS = "com.salesforce.Sfge";
const SFGE_WRAPPER_LIB_FOLDER = node_path_1.default.resolve(__dirname, '..', 'dist', 'java-lib');
const SFCA_REALTIME_START = 'SFCA-REALTIME-START';
const SFCA_REALTIME_END = 'SFCA-REALTIME-END';
const SFGE_ERROR_START = 'SfgeErrorStart';
class RuntimeSfgeWrapper {
    javaCommandExecutor;
    logFileName;
    temporaryWorkingDir;
    emitLogEvent;
    emitTelemetryEvent;
    constructor(javaCommandExecutor, clock, emitLogEvent, emitTelemetryEvent) {
        this.javaCommandExecutor = javaCommandExecutor;
        this.logFileName = `sfca-sfge-${(0, utils_1.formatToDateTimeString)(clock.now())}.log`;
        this.emitLogEvent = emitLogEvent;
        this.emitTelemetryEvent = emitTelemetryEvent;
    }
    async invokeDescribeCommand(workingFolder, emitProgress, logFolder) {
        const logFilePath = node_path_1.default.join(logFolder, this.logFileName);
        const sfgeRulesOutputFile = node_path_1.default.join(workingFolder, 'ruleInfo.json');
        this.emitLogEvent(code_analyzer_engine_api_1.LogLevel.Debug, (0, messages_1.getMessage)('LoggingToFile', 'describe', logFilePath));
        emitProgress(10);
        const javaCmdArgs = [`-Dsfge_log_name=${logFilePath}`, SFGE_MAIN_JAVA_CLASS, 'catalog', 'all', sfgeRulesOutputFile];
        const javaClassPaths = [node_path_1.default.join(SFGE_WRAPPER_LIB_FOLDER, '*')];
        await this.javaCommandExecutor.exec(javaCmdArgs, javaClassPaths);
        emitProgress(80);
        try {
            const sfgeRulesFileContents = await node_fs_1.default.promises.readFile(sfgeRulesOutputFile, 'utf-8');
            emitProgress(90);
            const sfgeRuleInfoList = JSON.parse(sfgeRulesFileContents);
            emitProgress(100);
            return sfgeRuleInfoList;
        }
        catch (err) /* istanbul ignore next */ {
            const errMsg = err instanceof Error ? err.message : String(err);
            throw new Error((0, code_analyzer_engine_api_1.getMessageFromCatalog)(code_analyzer_engine_api_1.SHARED_MESSAGE_CATALOG, 'ErrorParsingOutputFile', sfgeRulesOutputFile, errMsg), { cause: err });
        }
    }
    async invokeRunCommand(selectedRuleInfos, targetPaths, projectFilePaths, sfgeRunOptions, workingFolder, emitProgress) {
        emitProgress(2);
        const inputFileName = node_path_1.default.join(workingFolder, 'sfgeInput.json');
        const logFilePath = node_path_1.default.join(sfgeRunOptions.logFolder, this.logFileName);
        const ruleNames = selectedRuleInfos.map(sri => sri.name);
        await this.createSfgeInputFile(inputFileName, ruleNames, targetPaths, projectFilePaths);
        const resultsOutputFile = node_path_1.default.join(workingFolder, 'resultsFile.json');
        this.emitLogEvent(code_analyzer_engine_api_1.LogLevel.Debug, (0, messages_1.getMessage)('LoggingToFile', 'run', logFilePath));
        emitProgress(10);
        const javaCmdArgs = [`-Dsfge_log_name=${logFilePath}`];
        if (sfgeRunOptions.disableLimitReachedViolations) {
            // If the path expansion limit is set to -1, then there is no limit, and path expansion will continue unabated
            // instead of throwing LimitReached violations.
            javaCmdArgs.push('-DSFGE_PATH_EXPANSION_LIMIT=-1');
        }
        if (sfgeRunOptions.heapSizeArg) {
            javaCmdArgs.push(`-Xmx${sfgeRunOptions.heapSizeArg}`);
        }
        javaCmdArgs.push(`-DSFGE_RULE_THREAD_COUNT=${sfgeRunOptions.threadCount}`);
        javaCmdArgs.push(`-DSFGE_RULE_THREAD_TIMEOUT=${sfgeRunOptions.threadTimeout}`);
        javaCmdArgs.push(SFGE_MAIN_JAVA_CLASS, 'execute', inputFileName, resultsOutputFile);
        const javaClassPaths = [node_path_1.default.join(SFGE_WRAPPER_LIB_FOLDER, '*')];
        try {
            await this.javaCommandExecutor.exec(javaCmdArgs, javaClassPaths, (stdOutMsg) => handleRunStdOut(stdOutMsg, this.emitLogEvent, emitProgress, this.emitTelemetryEvent));
        }
        catch (err) {
            const errMsg = err instanceof Error ? err.message : /* istanbul ignore next */ String(err);
            const processedErrMsg = processMessageFromFailedRun(errMsg);
            throw new Error(processedErrMsg, { cause: err });
        }
        try {
            const resultsFileContents = await node_fs_1.default.promises.readFile(resultsOutputFile, 'utf-8');
            emitProgress(95);
            const sfgeResults = JSON.parse(resultsFileContents);
            emitProgress(100);
            return sfgeResults;
        }
        catch (err) /* istanbul ignore next */ {
            const errMsg = err instanceof Error ? err.message : String(err);
            throw new Error((0, code_analyzer_engine_api_1.getMessageFromCatalog)(code_analyzer_engine_api_1.SHARED_MESSAGE_CATALOG, 'ErrorParsingOutputFile', resultsOutputFile, errMsg), { cause: err });
        }
    }
    async createSfgeInputFile(filePath, rules, targets, allWorkspaceFiles) {
        const sfgeTargets = targets.map(target => {
            return {
                targetFile: target,
                targetMethods: []
            };
        });
        const inputFileContents = {
            targets: sfgeTargets,
            projectFilesAndFolders: allWorkspaceFiles,
            rulesToRun: rules
        };
        return node_fs_1.default.promises.writeFile(filePath, JSON.stringify(inputFileContents));
    }
}
exports.RuntimeSfgeWrapper = RuntimeSfgeWrapper;
function handleRunStdOut(stdOutMsg, emitLog, emitProgress, emitTelemetry) {
    if (stdOutMsg.startsWith(SFCA_REALTIME_START) && stdOutMsg.endsWith(SFCA_REALTIME_END)) {
        const sfgeMessages = JSON.parse(stdOutMsg.slice(SFCA_REALTIME_START.length, stdOutMsg.length - SFCA_REALTIME_END.length));
        for (const sfgeMessage of sfgeMessages) {
            if (isSfgeLogMessage(sfgeMessage)) {
                if (sfgeMessage.messageSeverity === 'TELEMETRY') {
                    try {
                        const telemetryData = JSON.parse(sfgeMessage.args[0]);
                        emitTelemetry(telemetryData.eventName, telemetryData);
                    }
                    catch (e) /* istanbul ignore next */ {
                        const message = e instanceof Error ? e.message : e;
                        emitLog(code_analyzer_engine_api_1.LogLevel.Fine, (0, messages_1.getMessage)('error_failed_to_parse_telemetry', message));
                    }
                }
                else {
                    const processedMessage = (0, messages_1.getMessage)(sfgeMessage.messageKey, ...sfgeMessage.args);
                    emitLog(sfgeLogLevelToSfcaLogLevel(sfgeMessage.messageSeverity), processedMessage);
                }
            }
            else if (isSfgeProgressMessage(sfgeMessage)) {
                const processedMessage = (0, messages_1.getMessage)(sfgeMessage.messageKey, ...sfgeMessage.args);
                const completionPercent = sfgeMessage.progressPercent;
                emitProgress(10 + 85 * completionPercent / 100, processedMessage); // 10%-95%
            }
        }
    }
}
function processMessageFromFailedRun(errMsg) {
    const indexOfErrorDelimiter = errMsg.indexOf(SFGE_ERROR_START);
    return indexOfErrorDelimiter > 0
        // If the error message includes our error delimiter string, then we should pull out that error
        // and wrap it in something slightly easier for the user to read.
        ? (0, messages_1.getMessage)('error_external_sfgeIncompleteAnalysis', errMsg.slice(indexOfErrorDelimiter + SFGE_ERROR_START.length).trim())
        // Otherwise, keep the error message styled as it came back from the Java Command Executor.
        : /* istanbul ignore next */ errMsg;
}
function isSfgeLogMessage(sfgeMessage) {
    return 'messageSeverity' in sfgeMessage;
}
function isSfgeProgressMessage(sfgeMessage) {
    return 'progressPercent' in sfgeMessage;
}
function sfgeLogLevelToSfcaLogLevel(sfgeLogLevel) {
    switch (sfgeLogLevel.toLowerCase()) {
        // istanbul ignore next
        case 'error':
            return code_analyzer_engine_api_1.LogLevel.Error;
        // istanbul ignore next
        case 'warning':
            return code_analyzer_engine_api_1.LogLevel.Warn;
        // istanbul ignore next
        case 'info':
            return code_analyzer_engine_api_1.LogLevel.Info;
        case 'debug':
            return code_analyzer_engine_api_1.LogLevel.Debug;
        // istanbul ignore next
        default:
            throw new Error(`Developer error: unexpected Log4J log level '${sfgeLogLevel}`);
    }
}
//# sourceMappingURL=sfge-wrapper.js.map