"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.executeWorkflow = exports.createExecuteWorkflowTool = void 0;
const n8n_workflow_1 = require("n8n-workflow");
const zod_1 = __importDefault(require("zod"));
const mcp_constants_1 = require("../mcp.constants");
const mcp_errors_1 = require("../mcp.errors");
const mcp_utils_1 = require("../mcp.utils");
const workflow_validation_utils_1 = require("./workflow-validation.utils");
const inputSchema = zod_1.default.object({
    workflowId: zod_1.default.string().describe('The ID of the workflow to execute'),
    executionMode: zod_1.default
        .enum(['manual', 'production'])
        .optional()
        .default('production')
        .describe('Use "manual" to test the current version of the workflow. Use "production" to execute the published (active) version.'),
    inputs: zod_1.default
        .discriminatedUnion('type', [
        zod_1.default.object({
            type: zod_1.default.literal('chat'),
            chatInput: zod_1.default.string().describe('Input for chat-based workflows'),
        }),
        zod_1.default.object({
            type: zod_1.default.literal('form'),
            formData: zod_1.default.record(zod_1.default.unknown()).describe('Input data for form-based workflows'),
        }),
        zod_1.default.object({
            type: zod_1.default.literal('webhook'),
            webhookData: zod_1.default
                .object({
                method: zod_1.default
                    .enum(['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'])
                    .optional()
                    .default('GET')
                    .describe('HTTP method (defaults to GET)'),
                query: zod_1.default.record(zod_1.default.string()).optional().describe('Query string parameters'),
                body: zod_1.default
                    .record(zod_1.default.unknown())
                    .optional()
                    .describe('Request body data (main webhook payload)'),
                headers: zod_1.default
                    .record(zod_1.default.string())
                    .optional()
                    .describe('HTTP headers (e.g., authorization, content-type)'),
            })
                .describe('Input data for webhook-based workflows'),
        }),
    ])
        .optional()
        .describe('Inputs to provide to the workflow.'),
});
const outputSchema = {
    executionId: zod_1.default.string().nullable(),
    status: zod_1.default.enum(['started', 'error']).describe('The status of the execution'),
    error: zod_1.default.string().optional().describe('Error message if the execution failed'),
};
const createExecuteWorkflowTool = (user, workflowFinderService, workflowRunner, telemetry, mcpService) => ({
    name: 'execute_workflow',
    config: {
        description: 'Execute a workflow by ID. Returns the execution ID immediately without waiting for completion. Before executing always ensure you know the input schema by first using the get_workflow_details tool and consulting workflow description',
        inputSchema: inputSchema.shape,
        outputSchema,
        annotations: {
            title: 'Execute Workflow',
            readOnlyHint: false,
            destructiveHint: true,
            idempotentHint: false,
            openWorldHint: true,
        },
    },
    handler: async ({ workflowId, executionMode, inputs }) => {
        const telemetryPayload = {
            user_id: user.id,
            tool_name: 'execute_workflow',
            parameters: { workflowId, executionMode, inputs: getInputMetaData(inputs) },
        };
        try {
            const output = await (0, exports.executeWorkflow)(user, workflowFinderService, workflowRunner, mcpService, workflowId, inputs, executionMode);
            telemetryPayload.results = {
                success: true,
                data: {
                    executionId: output.executionId,
                    status: output.status,
                },
            };
            telemetry.track(mcp_constants_1.USER_CALLED_MCP_TOOL_EVENT, telemetryPayload);
            return {
                content: [{ type: 'text', text: (0, n8n_workflow_1.jsonStringify)(output) }],
                structuredContent: output,
            };
        }
        catch (er) {
            const error = (0, n8n_workflow_1.ensureError)(er);
            const isAccessError = error instanceof mcp_errors_1.WorkflowAccessError;
            const errorInfo = {
                message: error.message || 'Unknown error',
                name: error.constructor.name,
            };
            if ('extra' in error && error.extra) {
                errorInfo.extra = error.extra;
            }
            if (error.cause) {
                errorInfo.cause =
                    error.cause instanceof Error ? error.cause.message : (0, n8n_workflow_1.jsonStringify)(error.cause);
            }
            const output = {
                executionId: null,
                status: 'error',
                error: error.message ?? `${error.constructor.name}: (no message)`,
            };
            telemetryPayload.results = {
                success: false,
                error: errorInfo,
                error_reason: isAccessError ? error.reason : undefined,
            };
            telemetry.track(mcp_constants_1.USER_CALLED_MCP_TOOL_EVENT, telemetryPayload);
            return {
                content: [{ type: 'text', text: (0, n8n_workflow_1.jsonStringify)(output) }],
                structuredContent: output,
            };
        }
    },
});
exports.createExecuteWorkflowTool = createExecuteWorkflowTool;
const executeWorkflow = async (user, workflowFinderService, workflowRunner, mcpService, workflowId, inputs, executionMode = 'production') => {
    const workflow = await (0, workflow_validation_utils_1.getMcpWorkflow)(workflowId, user, ['workflow:execute'], workflowFinderService, { includeActiveVersion: true });
    const runData = await buildRunData(workflow, user.id, workflowId, executionMode, inputs, mcpService);
    const executionId = await workflowRunner.run(runData);
    return {
        executionId,
        status: 'started',
    };
};
exports.executeWorkflow = executeWorkflow;
const getVersionDataForExecution = (workflow, workflowId, executionMode) => {
    if (executionMode === 'production' && !workflow.activeVersionId) {
        throw new mcp_errors_1.WorkflowAccessError(`Workflow '${workflowId}' has no published (active) version to execute`, 'workflow_not_active');
    }
    const nodes = executionMode === 'production' ? (workflow.activeVersion?.nodes ?? []) : (workflow.nodes ?? []);
    const connections = executionMode === 'production'
        ? (workflow.activeVersion?.connections ?? {})
        : (workflow.connections ?? {});
    return { nodes, connections };
};
const buildRunData = async (workflow, userId, workflowId, executionMode, inputs, mcpService) => {
    const { nodes, connections } = getVersionDataForExecution(workflow, workflowId, executionMode);
    const triggerNode = (0, mcp_utils_1.findMcpSupportedTrigger)(nodes, executionMode);
    if (!triggerNode) {
        throw new mcp_errors_1.WorkflowAccessError(`Only workflows with the following trigger nodes can be executed: ${getSupportedTriggerNamesForMode(executionMode).join(', ')}.`, 'unsupported_trigger');
    }
    const mcpMessageId = `mcp-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
    const isManualExecution = executionMode === 'manual';
    const runData = {
        executionMode: isManualExecution ? 'manual' : getExecutionModeForTrigger(triggerNode),
        workflowData: { ...workflow, nodes, connections },
        userId,
        isMcpExecution: mcpService.isQueueMode,
        mcpType: 'service',
        mcpSessionId: mcpMessageId,
        mcpMessageId,
    };
    runData.startNodes = [{ name: triggerNode.name, sourceData: null }];
    const triggerPinData = await getPinDataForTrigger(triggerNode, inputs);
    const workflowPinData = isManualExecution ? (workflow.pinData ?? {}) : {};
    runData.pinData = { ...workflowPinData, ...triggerPinData };
    runData.executionData = (0, n8n_workflow_1.createRunExecutionData)({
        startData: {},
        resultData: {
            pinData: runData.pinData,
            runData: {},
        },
        executionData: {
            contextData: {},
            metadata: {},
            nodeExecutionStack: [
                {
                    node: triggerNode,
                    data: {
                        main: [runData.pinData[triggerNode.name]],
                    },
                    source: null,
                },
            ],
            waitingExecution: {},
            waitingExecutionSource: {},
        },
    });
    return runData;
};
const getExecutionModeForTrigger = (node) => {
    switch (node.type) {
        case n8n_workflow_1.WEBHOOK_NODE_TYPE:
            return 'webhook';
        case n8n_workflow_1.CHAT_TRIGGER_NODE_TYPE:
            return 'chat';
        case n8n_workflow_1.MANUAL_TRIGGER_NODE_TYPE:
            return 'manual';
        case n8n_workflow_1.FORM_TRIGGER_NODE_TYPE:
            return 'trigger';
        default:
            return 'trigger';
    }
};
const getPinDataForTrigger = async (node, inputs) => {
    switch (node.type) {
        case n8n_workflow_1.MANUAL_TRIGGER_NODE_TYPE:
            return {
                [node.name]: [{ json: {} }],
            };
        case n8n_workflow_1.WEBHOOK_NODE_TYPE: {
            const webhookData = inputs?.type === 'webhook' ? inputs.webhookData : undefined;
            return {
                [node.name]: [
                    {
                        json: {
                            headers: webhookData?.headers ?? {},
                            query: webhookData?.query ?? {},
                            body: webhookData?.body ?? {},
                        },
                    },
                ],
            };
        }
        case n8n_workflow_1.CHAT_TRIGGER_NODE_TYPE:
            if (!inputs || inputs.type !== 'chat')
                return { [node.name]: [{ json: {} }] };
            return {
                [node.name]: [
                    {
                        json: {
                            sessionId: `mcp-session-${Date.now()}`,
                            action: 'sendMessage',
                            chatInput: inputs.chatInput,
                        },
                    },
                ],
            };
        case n8n_workflow_1.FORM_TRIGGER_NODE_TYPE:
            if (!inputs || inputs.type !== 'form')
                return { [node.name]: [{ json: {} }] };
            return {
                [node.name]: [
                    {
                        json: {
                            submittedAt: new Date().toISOString(),
                            formMode: 'mcp',
                            ...(inputs.formData ?? {}),
                        },
                    },
                ],
            };
        case n8n_workflow_1.SCHEDULE_TRIGGER_NODE_TYPE: {
            const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
            const moment = (await Promise.resolve().then(() => __importStar(require('moment-timezone')))).default;
            const momentTz = moment.tz(timezone);
            return {
                [node.name]: [
                    {
                        json: {
                            timestamp: momentTz.toISOString(true),
                            'Readable date': momentTz.format('MMMM Do YYYY, h:mm:ss a'),
                            'Readable time': momentTz.format('h:mm:ss a'),
                            'Day of week': momentTz.format('dddd'),
                            Year: momentTz.format('YYYY'),
                            Month: momentTz.format('MMMM'),
                            'Day of month': momentTz.format('DD'),
                            Hour: momentTz.format('HH'),
                            Minute: momentTz.format('mm'),
                            Second: momentTz.format('ss'),
                            Timezone: `${timezone} (UTC${momentTz.format('Z')})`,
                        },
                    },
                ],
            };
        }
        default:
            return { [node.name]: [{ json: {} }] };
    }
};
const getSupportedTriggerNamesForMode = (executionMode) => {
    return executionMode === 'production'
        ? Object.values(mcp_constants_1.SUPPORTED_PRODUCTION_MCP_TRIGGERS)
        : Object.values(mcp_constants_1.SUPPORTED_MCP_TRIGGERS);
};
const getInputMetaData = (inputs) => {
    if (!inputs) {
        return undefined;
    }
    switch (inputs.type) {
        case 'chat':
            return {
                type: 'chat',
                parameter_count: 1,
            };
        case 'form':
            return {
                type: 'form',
                parameter_count: Object.keys(inputs.formData ?? {}).length,
            };
        case 'webhook':
            return {
                type: 'webhook',
                parameter_count: [
                    inputs.webhookData?.body ? Object.keys(inputs.webhookData.body).length : 0,
                    inputs.webhookData?.query ? Object.keys(inputs.webhookData.query).length : 0,
                    inputs.webhookData?.headers ? Object.keys(inputs.webhookData.headers).length : 0,
                ].reduce((a, b) => a + b, 0),
            };
        default:
            return undefined;
    }
};
//# sourceMappingURL=execute-workflow.tool.js.map