import { BUILT_IN_AGENT_TOOLS, getNodesForExecutionScope } from '@agent-spaces/shared';
import * as workflowService from '../workflow.js';
let workflowExecutionManager = null;
const DEFAULT_SYNC_TIMEOUT_MS = 120_000;
const MAX_SYNC_TIMEOUT_MS = 600_000;
const POLL_INTERVAL_MS = 500;
const pagingInputSchema = schema({
    page: { type: 'number', description: 'Page number, starting from 1. Defaults to 1.' },
    page_size: { type: 'number', description: 'Page size. Defaults to 10, max 50.' },
});
const executeWorkflowInputSchema = schema({
    workflow_id: { type: 'string', description: 'Workflow ID. workflowId is also accepted.' },
    workflowId: { type: 'string', description: 'Workflow ID alias.' },
    input: { type: 'object', description: 'Workflow input object.', properties: {} },
    start_node_id: { type: 'string', description: 'Optional start node ID when the workflow has multiple start nodes.' },
    startNodeId: { type: 'string', description: 'Start node ID alias.' },
    max_wait_ms: { type: 'number', description: `Sync execution wait timeout. Defaults to ${DEFAULT_SYNC_TIMEOUT_MS}, max ${MAX_SYNC_TIMEOUT_MS}.` },
});
export function setWorkflowExecutionManager(manager) {
    workflowExecutionManager = manager;
}
export function createWorkflowExecutionFunctionTools(allowedTools) {
    const allowedToolNames = getAllowedWorkflowToolNames(allowedTools);
    const tools = [
        {
            name: 'list_workflows',
            description: 'List saved workflows, newest updated first. Returns workflow IDs, descriptions, start nodes, and input fields.',
            inputSchema: pagingInputSchema,
            annotations: { readOnly: true, openWorld: false },
            execute: async (input) => listWorkflows(input),
        },
        {
            name: 'search_workflow',
            description: 'Search saved workflows by keyword in workflow name or description.',
            inputSchema: schema({
                keyword: { type: 'string', description: 'Keyword to search in workflow name and description.' },
                page: { type: 'number', description: 'Page number, starting from 1. Defaults to 1.' },
                page_size: { type: 'number', description: 'Page size. Defaults to 10, max 50.' },
            }, ['keyword']),
            annotations: { readOnly: true, openWorld: false },
            execute: async (input) => searchWorkflow(input),
        },
        {
            name: 'execute_workflow_sync',
            description: 'Start a workflow and wait until it completes, errors, pauses, or reaches max_wait_ms. Returns execution steps.',
            inputSchema: executeWorkflowInputSchema,
            annotations: { destructive: false, openWorld: false },
            execute: async (input) => executeWorkflowSync(input),
        },
        {
            name: 'execute_workflow_async',
            description: 'Start a workflow without waiting for completion. Use get_workflow_result with the returned executionId.',
            inputSchema: executeWorkflowInputSchema,
            annotations: { destructive: false, openWorld: false },
            execute: async (input) => executeWorkflowAsync(input),
        },
        {
            name: 'get_workflow_result',
            description: 'Read workflow execution result by execution_id. Optionally filter steps by node_id.',
            inputSchema: schema({
                execution_id: { type: 'string', description: 'Execution ID returned by execute_workflow_sync or execute_workflow_async.' },
                workflow_id: { type: 'string', description: 'Optional workflow ID to narrow the lookup.' },
                workflowId: { type: 'string', description: 'Workflow ID alias.' },
                node_id: { type: 'string', description: 'Optional node ID. If provided, only that node step is returned.' },
            }, ['execution_id']),
            annotations: { readOnly: true, openWorld: false },
            execute: async (input) => getWorkflowResult(input),
        },
        {
            name: 'get_workflow_latest_result',
            description: 'Read the latest execution result for a workflow. Optionally filter steps by node_id.',
            inputSchema: schema({
                workflow_id: { type: 'string', description: 'Workflow ID. workflowId is also accepted.' },
                workflowId: { type: 'string', description: 'Workflow ID alias.' },
                node_id: { type: 'string', description: 'Optional node ID. If provided, only that node step is returned.' },
            }),
            annotations: { readOnly: true, openWorld: false },
            execute: async (input) => getWorkflowLatestResult(input),
        },
    ];
    return tools.filter((tool) => allowedToolNames.has(tool.name));
}
function listWorkflows(input) {
    const record = asRecord(input);
    const { page, pageSize } = pageArgs(record);
    const workflows = workflowService.listWorkflows().sort((a, b) => b.updatedAt - a.updatedAt);
    const total = workflows.length;
    const totalPages = Math.max(1, Math.ceil(total / pageSize));
    const currentPage = Math.min(page, totalPages);
    const items = workflows.slice((currentPage - 1) * pageSize, currentPage * pageSize).map(summarizeWorkflowForTool);
    return {
        success: true,
        message: `Page ${currentPage} of ${totalPages}.`,
        data: { page: currentPage, page_size: pageSize, total, total_pages: totalPages, workflows: items },
    };
}
function searchWorkflow(input) {
    const record = asRecord(input);
    const keyword = stringInput(record, 'keyword')?.toLowerCase();
    if (!keyword)
        return { success: false, message: 'keyword is required' };
    const { page, pageSize } = pageArgs(record);
    const matches = workflowService.listWorkflows()
        .filter((workflow) => [workflow.name, workflow.description ?? ''].join('\n').toLowerCase().includes(keyword))
        .sort((a, b) => b.updatedAt - a.updatedAt)
        .map(summarizeWorkflowForTool);
    const total = matches.length;
    const totalPages = Math.max(1, Math.ceil(total / pageSize));
    const currentPage = Math.min(page, totalPages);
    const items = matches.slice((currentPage - 1) * pageSize, currentPage * pageSize);
    return {
        success: true,
        message: `Found ${total} matching workflows. Page ${currentPage} of ${totalPages}.`,
        data: { page: currentPage, page_size: pageSize, total, total_pages: totalPages, workflows: items },
    };
}
async function executeWorkflowAsync(input) {
    const manager = requireWorkflowExecutionManager();
    if ('success' in manager)
        return manager;
    const record = asRecord(input);
    const workflowId = resolveWorkflowId(record);
    const validation = validateWorkflowId(workflowId);
    if (validation)
        return validation;
    const result = await manager.execute({
        workflowId,
        input: objectInput(record, 'input'),
        startNodeId: stringInput(record, 'start_node_id') ?? stringInput(record, 'startNodeId'),
    }, 'agent-tools');
    return {
        success: true,
        message: 'Workflow execution started.',
        data: { workflow_id: workflowId, executionId: result.executionId, status: result.status },
    };
}
async function executeWorkflowSync(input) {
    const manager = requireWorkflowExecutionManager();
    if ('success' in manager)
        return manager;
    const record = asRecord(input);
    const workflowId = resolveWorkflowId(record);
    const validation = validateWorkflowId(workflowId);
    if (validation)
        return validation;
    const result = await manager.execute({
        workflowId,
        input: objectInput(record, 'input'),
        startNodeId: stringInput(record, 'start_node_id') ?? stringInput(record, 'startNodeId'),
    }, 'agent-tools');
    const timeoutMs = Math.min(MAX_SYNC_TIMEOUT_MS, Math.max(POLL_INTERVAL_MS, numberInput(record, 'max_wait_ms', DEFAULT_SYNC_TIMEOUT_MS)));
    const startedAt = Date.now();
    let log = null;
    let status = result.status;
    while (Date.now() - startedAt < timeoutMs) {
        const recovery = manager.getExecutionRecovery({ workflowId, executionId: result.executionId }, 'agent-tools');
        log = recovery.execution?.log ?? workflowService.getExecutionLog(workflowId, result.executionId);
        status = log?.status ?? recovery.execution?.status ?? status;
        if (status !== 'running')
            break;
        await sleep(POLL_INTERVAL_MS);
    }
    log = log ?? workflowService.getExecutionLog(workflowId, result.executionId);
    status = log?.status ?? status;
    const timedOut = status === 'running';
    return {
        success: true,
        message: timedOut ? `Workflow is still running after ${timeoutMs}ms.` : `Workflow finished with status: ${status}.`,
        data: {
            workflow_id: workflowId,
            executionId: result.executionId,
            status,
            timedOut,
            steps: log ? formatExecutionSteps(log) : [],
        },
    };
}
function getWorkflowResult(input) {
    const record = asRecord(input);
    const executionId = stringInput(record, 'execution_id');
    if (!executionId)
        return { success: false, message: 'execution_id is required' };
    const workflowId = resolveWorkflowId(record);
    const nodeId = stringInput(record, 'node_id');
    const scopedLog = workflowId ? workflowService.getExecutionLog(workflowId, executionId) : null;
    if (workflowId && scopedLog)
        return executionLogResult(workflowId, scopedLog, nodeId);
    for (const workflow of workflowService.listWorkflows()) {
        const log = workflowService.getExecutionLog(workflow.id, executionId);
        if (log)
            return executionLogResult(workflow.id, log, nodeId);
    }
    return { success: false, message: `Execution log not found: ${executionId}` };
}
function getWorkflowLatestResult(input) {
    const record = asRecord(input);
    const workflowId = resolveWorkflowId(record);
    const validation = validateWorkflowId(workflowId);
    if (validation)
        return validation;
    const log = workflowService.listExecutionLogs(workflowId)[0] ?? null;
    if (!log)
        return { success: false, message: `Workflow has no execution logs: ${workflowId}` };
    return executionLogResult(workflowId, log, stringInput(record, 'node_id'), true);
}
function executionLogResult(workflowId, log, nodeId, latest = false) {
    return {
        success: true,
        message: `${latest ? 'Latest execution status' : 'Execution status'}: ${log.status}.`,
        data: {
            workflow_id: workflowId,
            executionId: log.id,
            status: log.status,
            startedAt: log.startedAt,
            finishedAt: log.finishedAt,
            steps: formatExecutionSteps(log, nodeId),
        },
    };
}
function summarizeWorkflowForTool(workflow) {
    const startNodes = getNodesForExecutionScope(workflow.nodes, null)
        .filter((node) => node.type === 'start')
        .map((node) => ({
        id: node.id,
        label: node.label,
        inputFields: Array.isArray(node.data?.inputFields) ? clone(node.data.inputFields) : [],
    }));
    return {
        workflow_id: workflow.id,
        title: workflow.name,
        description: workflow.description ?? '',
        inputFields: startNodes[0]?.inputFields ?? [],
        startNodes,
        updatedAt: workflow.updatedAt,
    };
}
function formatExecutionSteps(log, nodeId) {
    return log.steps
        .filter((step) => !nodeId || step.nodeId === nodeId)
        .map((step) => ({
        nodeId: step.nodeId,
        nodeLabel: step.nodeLabel,
        status: step.status,
        startedAt: step.startedAt,
        finishedAt: step.finishedAt,
        duration: step.finishedAt ? step.finishedAt - step.startedAt : undefined,
        input: step.input,
        output: step.output,
        error: step.error,
        logs: step.logs,
    }));
}
function validateWorkflowId(workflowId) {
    if (!workflowId)
        return { success: false, message: 'workflow_id is required' };
    if (!workflowService.getWorkflow(workflowId))
        return { success: false, message: `Workflow not found: ${workflowId}` };
    return null;
}
function requireWorkflowExecutionManager() {
    if (!workflowExecutionManager)
        return { success: false, message: 'Workflow execution manager is not initialized' };
    return workflowExecutionManager;
}
function getAllowedWorkflowToolNames(allowedTools) {
    const names = new Set(allowedTools ?? BUILT_IN_AGENT_TOOLS.map((tool) => tool.name));
    const hasWorkflowTools = Array.from(names).some((name) => isWorkflowExecutionToolName(name));
    if (hasWorkflowTools) {
        names.add('list_workflows');
        names.add('search_workflow');
        names.add('get_workflow_result');
        names.add('get_workflow_latest_result');
    }
    return names;
}
function isWorkflowExecutionToolName(name) {
    return name === 'list_workflows'
        || name === 'search_workflow'
        || name === 'execute_workflow_sync'
        || name === 'execute_workflow_async'
        || name === 'get_workflow_result'
        || name === 'get_workflow_latest_result';
}
function pageArgs(input) {
    return {
        page: Math.max(1, Math.floor(numberInput(input, 'page', 1))),
        pageSize: Math.min(50, Math.max(1, Math.floor(numberInput(input, 'page_size', 10)))),
    };
}
function resolveWorkflowId(input) {
    return stringInput(input, 'workflow_id') ?? stringInput(input, 'workflowId') ?? '';
}
function schema(properties, required) {
    return { type: 'object', properties, required, additionalProperties: false };
}
function asRecord(input) {
    return input && typeof input === 'object' && !Array.isArray(input) ? input : {};
}
function stringInput(input, key) {
    const value = input[key];
    if (typeof value !== 'string')
        return undefined;
    const trimmed = value.trim();
    return trimmed || undefined;
}
function numberInput(input, key, fallback) {
    const value = input[key];
    return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
}
function objectInput(input, key) {
    const value = input[key];
    return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
}
function clone(value) {
    return JSON.parse(JSON.stringify(value));
}
function sleep(ms) {
    return new Promise((resolve) => setTimeout(resolve, ms));
}
//# sourceMappingURL=workflow-exec-tools.js.map