"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 path_1 = __importDefault(require("path"));
const utils_1 = require("./utils");
const process_manager_1 = require("./process-communication/process-manager");
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, lockedData } = options;
    const logger = options.logger.child({ step: step.config.name });
    const flows = step.config.flows;
    return new Promise((resolve, reject) => {
        const streamConfig = lockedData.getStreams();
        const streams = Object.keys(streamConfig).map((name) => ({ name }));
        const jsonData = JSON.stringify({ data, flows, traceId, contextInFirstArg, streams });
        const { runner, command, args } = getLanguageBasedRunner(step.filePath);
        let result;
        // Create process manager with unified communication handling
        const processManager = new process_manager_1.ProcessManager({
            command,
            args: [...args, runner, step.filePath, jsonData],
            logger,
            context: 'StepExecution'
        });
        processManager.spawn().then(() => {
            // Register all step handlers
            processManager.handler('close', async () => processManager.kill());
            processManager.handler('log', async (input) => logger.log(input));
            processManager.handler('state.get', (input) => state.get(input.traceId, input.key));
            processManager.handler('state.set', (input) => state.set(input.traceId, input.key, input.value));
            processManager.handler('state.delete', (input) => state.delete(input.traceId, input.key));
            processManager.handler('state.clear', (input) => state.clear(input.traceId));
            processManager.handler('result', async (input) => {
                result = input;
            });
            processManager.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);
            });
            Object.entries(streamConfig).forEach(([name, streamFactory]) => {
                const stateStream = streamFactory();
                processManager.handler(`streams.${name}.get`, (input) => stateStream.get(input.id));
                processManager.handler(`streams.${name}.update`, (input) => stateStream.update(input.id, input.data));
                processManager.handler(`streams.${name}.delete`, (input) => stateStream.delete(input.id));
                processManager.handler(`streams.${name}.create`, (input) => stateStream.create(input.id, input.data));
            });
            if (processManager.commType === 'ipc') {
                // Handle stdout for non-RPC mode (logging)
                processManager.onStdout((data) => {
                    try {
                        const message = JSON.parse(data.toString());
                        logger.log(message);
                    }
                    catch {
                        logger.info(Buffer.from(data).toString());
                    }
                });
            }
            // Handle stderr
            processManager.onStderr((data) => logger.error(Buffer.from(data).toString()));
            // Handle process close
            processManager.onProcessClose((code) => {
                processManager.close();
                if (code !== 0 && code !== null) {
                    reject(`Process exited with code ${code}`);
                }
                else {
                    resolve(result);
                }
            });
            // Handle process errors
            processManager.onProcessError((error) => {
                processManager.close();
                if (error.code === 'ENOENT') {
                    reject(`Executable ${command} not found`);
                }
                else {
                    reject(error);
                }
            });
        }).catch((error) => {
            reject(`Failed to spawn process: ${error}`);
        });
    });
};
exports.callStepFile = callStepFile;
