"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.callStepFile = void 0;
const step_handler_rpc_processor_1 = require("./step-handler-rpc-processor");
const child_process_1 = require("child_process");
const path_1 = __importDefault(require("path"));
const utils_1 = require("./utils");
const getLanguageBasedRunner = (stepFilePath = '') => {
    const isPython = stepFilePath.endsWith('.py');
    const isRuby = stepFilePath.endsWith('.rb');
    const isNode = stepFilePath.endsWith('.js') || stepFilePath.endsWith('.ts');
    if (isPython) {
        const pythonRunner = path_1.default.join(__dirname, 'python', 'python-runner.py');
        return { runner: pythonRunner, command: 'python', args: [] };
    }
    else if (isRuby) {
        const rubyRunner = path_1.default.join(__dirname, 'ruby', 'ruby-runner.rb');
        return { runner: rubyRunner, command: 'ruby', args: [] };
    }
    else if (isNode) {
        if (process.env._MOTIA_TEST_MODE === 'true') {
            const nodeRunner = path_1.default.join(__dirname, 'node', 'node-runner.ts');
            return { runner: nodeRunner, command: 'node', args: ['-r', 'ts-node/register'] };
        }
        const nodeRunner = path_1.default.join(__dirname, 'node', 'node-runner.js');
        return { runner: nodeRunner, command: 'node', args: [] };
    }
    throw Error(`Unsupported file extension ${stepFilePath}`);
};
const callStepFile = (options) => {
    const { step, printer, eventManager, state, traceId, data, contextInFirstArg } = options;
    const logger = options.logger.child({ step: step.config.name });
    const flows = step.config.flows;
    return new Promise((resolve, reject) => {
        const jsonData = JSON.stringify({ data, flows, traceId, contextInFirstArg });
        const { runner, command, args } = getLanguageBasedRunner(step.filePath);
        let result;
        const child = (0, child_process_1.spawn)(command, [...args, runner, step.filePath, jsonData], {
            stdio: [undefined, undefined, undefined, 'ipc'],
        });
        const rpcProcessor = new step_handler_rpc_processor_1.RpcProcessor(child);
        rpcProcessor.handler('close', async () => child.kill());
        rpcProcessor.handler('log', async (input) => logger.log(input));
        rpcProcessor.handler('state.get', (input) => state.get(input.traceId, input.key));
        rpcProcessor.handler('state.set', (input) => state.set(input.traceId, input.key, input.value));
        rpcProcessor.handler('state.delete', (input) => state.delete(input.traceId, input.key));
        rpcProcessor.handler('state.clear', (input) => state.clear(input.traceId));
        rpcProcessor.handler('result', async (input) => {
            result = input;
        });
        rpcProcessor.handler('emit', async (input) => {
            if (!(0, utils_1.isAllowedToEmit)(step, input.topic)) {
                return printer.printInvalidEmit(step, input.topic);
            }
            return eventManager.emit({ ...input, traceId, flows: step.config.flows, logger }, step.filePath);
        });
        rpcProcessor.init();
        child.stdout?.on('data', (data) => {
            try {
                const message = JSON.parse(data.toString());
                logger.log(message);
            }
            catch {
                logger.info(Buffer.from(data).toString());
            }
        });
        child.stderr?.on('data', (data) => logger.error(Buffer.from(data).toString()));
        child.on('close', (code) => {
            if (code !== 0 && code !== null) {
                reject(`Process exited with code ${code}`);
            }
            else {
                resolve(result);
            }
        });
        child.on('error', (error) => {
            if (error.code === 'ENOENT') {
                reject(`Executable ${command} not found`);
            }
            else {
                reject(error);
            }
        });
    });
};
exports.callStepFile = callStepFile;
