import * as t from '@babel/types';
import { generateUniqueId } from '../runtime/context.js';
import { BatchParallelDetector } from './batch-detector.js';
import { RuntimeFunction } from '../runtime/runtime-functions.js';
const TOOL_OPERATION_CALL = 'call';
export class PromiseTransformer {
    transformCount = 0;
    batchDetector;
    enableBatchParallel;
    constructor(enableBatchParallel = true) {
        this.batchDetector = new BatchParallelDetector();
        this.enableBatchParallel = enableBatchParallel;
    }
    transformPromiseAll(path) {
        const node = path.node;
        if (!this.isPromiseAll(node)) {
            return false;
        }
        const arrayArg = node.arguments[0];
        if (this.enableBatchParallel && this.batchDetector.canBatch(node)) {
            return this.transformToBatchParallel(path, node);
        }
        if (t.isArrayExpression(arrayArg)) {
            return this.transformToSequential(path, node);
        }
        return false;
    }
    transformPromiseAllSettled(path) {
        const node = path.node;
        if (!this.isPromiseAllSettled(node)) {
            return false;
        }
        const arrayArg = node.arguments[0];
        if (t.isArrayExpression(arrayArg)) {
            const parallelId = generateUniqueId('allSettled');
            const runtimeCall = t.awaitExpression(t.callExpression(t.memberExpression(t.identifier('__runtime'), t.identifier(RuntimeFunction.RESUMABLE_PROMISE_ALL_SETTLED)), [arrayArg, t.stringLiteral(parallelId)]));
            path.replaceWith(runtimeCall);
            this.transformCount++;
            return true;
        }
        return false;
    }
    transformToBatchParallel(path, node) {
        const arrayArg = node.arguments[0];
        if (!t.isArrayExpression(arrayArg)) {
            return false;
        }
        const batchId = generateUniqueId('batch');
        const batchCallsArray = t.arrayExpression(arrayArg.elements.map((el) => {
            if (!el || t.isSpreadElement(el)) {
                return t.nullLiteral();
            }
            let callNode = el;
            if (t.isAwaitExpression(callNode)) {
                callNode = callNode.argument;
            }
            if (!t.isCallExpression(callNode) || !t.isMemberExpression(callNode.callee)) {
                return t.nullLiteral();
            }
            const callInfo = this.batchDetector.extractCallInfo(callNode);
            if (!callInfo) {
                return t.nullLiteral();
            }
            const payloadArg = callNode.arguments[0];
            return this.buildBatchCallObject(callInfo, payloadArg);
        }));
        const runtimeCall = t.awaitExpression(t.callExpression(t.memberExpression(t.identifier('__runtime'), t.identifier(RuntimeFunction.BATCH_PARALLEL)), [batchCallsArray, t.stringLiteral(batchId)]));
        path.replaceWith(runtimeCall);
        this.transformCount++;
        return true;
    }
    /**
     * Builds the AST for a batch call object.
     * For client tools, wraps payload with toolName and input structure.
     */
    buildBatchCallObject(callInfo, payloadArg) {
        const payloadExpr = payloadArg && t.isExpression(payloadArg) ? payloadArg : t.objectExpression([]);
        if (callInfo.type === 'tool') {
            // Client tools: operation is always 'call', payload contains toolName and input
            return t.objectExpression([
                t.objectProperty(t.identifier('type'), t.stringLiteral(callInfo.type)),
                t.objectProperty(t.identifier('operation'), t.stringLiteral(TOOL_OPERATION_CALL)),
                t.objectProperty(t.identifier('payload'), t.objectExpression([
                    t.objectProperty(t.identifier('toolName'), t.stringLiteral(callInfo.operation)),
                    t.objectProperty(t.identifier('input'), payloadExpr),
                ])),
            ]);
        }
        // Standard services (llm, approval, embedding)
        return t.objectExpression([
            t.objectProperty(t.identifier('type'), t.stringLiteral(callInfo.type)),
            t.objectProperty(t.identifier('operation'), t.stringLiteral(callInfo.operation)),
            t.objectProperty(t.identifier('payload'), payloadExpr),
        ]);
    }
    transformToSequential(path, node) {
        const arrayArg = node.arguments[0];
        if (!t.isArrayExpression(arrayArg)) {
            return false;
        }
        const parallelId = generateUniqueId('parallel');
        const runtimeCall = t.awaitExpression(t.callExpression(t.memberExpression(t.identifier('__runtime'), t.identifier(RuntimeFunction.RESUMABLE_PROMISE_ALL)), [arrayArg, t.stringLiteral(parallelId)]));
        path.replaceWith(runtimeCall);
        this.transformCount++;
        return true;
    }
    payloadToExpression(payload) {
        const properties = [];
        for (const [key, value] of Object.entries(payload)) {
            properties.push(t.objectProperty(t.identifier(key), this.valueToExpression(value)));
        }
        return t.objectExpression(properties);
    }
    valueToExpression(value) {
        if (typeof value === 'string') {
            return t.stringLiteral(value);
        }
        if (typeof value === 'number') {
            return t.numericLiteral(value);
        }
        if (typeof value === 'boolean') {
            return t.booleanLiteral(value);
        }
        if (value === null) {
            return t.nullLiteral();
        }
        if (Array.isArray(value)) {
            return t.arrayExpression(value.map((v) => this.valueToExpression(v)));
        }
        if (typeof value === 'object') {
            return this.payloadToExpression(value);
        }
        return t.identifier('undefined');
    }
    isPromiseAll(node) {
        const callee = node.callee;
        return (t.isMemberExpression(callee) &&
            t.isIdentifier(callee.object, { name: 'Promise' }) &&
            t.isIdentifier(callee.property, { name: 'all' }));
    }
    isPromiseAllSettled(node) {
        const callee = node.callee;
        return (t.isMemberExpression(callee) &&
            t.isIdentifier(callee.object, { name: 'Promise' }) &&
            t.isIdentifier(callee.property, { name: 'allSettled' }));
    }
    getTransformCount() {
        return this.transformCount;
    }
    resetTransformCount() {
        this.transformCount = 0;
    }
}
//# sourceMappingURL=promise-transformer.js.map