import DataLoader from 'dataloader';
import { ValueOrPromise } from 'value-or-promise';
import { getOperationASTFromRequest, isAsyncIterable, } from '@graphql-tools/utils';
import { mergeRequests } from './mergeRequests.js';
import { splitResult } from './splitResult.js';
export function createBatchingExecutor(executor, dataLoaderOptions, extensionsReducer = defaultExtensionsReducer) {
    const loadFn = createLoadFn(executor, extensionsReducer);
    const queryLoader = new DataLoader(loadFn, dataLoaderOptions);
    const mutationLoader = new DataLoader(loadFn, dataLoaderOptions);
    return function batchingExecutor(request) {
        const operationType = request.operationType ?? getOperationASTFromRequest(request)?.operation;
        switch (operationType) {
            case 'query':
                return queryLoader.load(request);
            case 'mutation':
                return mutationLoader.load(request);
            case 'subscription':
                return executor(request);
            default:
                throw new Error(`Invalid operation type "${operationType}"`);
        }
    };
}
function createLoadFn(executor, extensionsReducer) {
    return function batchExecuteLoadFn(requests) {
        if (requests.length === 1) {
            return new ValueOrPromise(() => executor(requests[0]))
                .then((result) => [result])
                .catch((err) => [err]);
        }
        const mergedRequests = mergeRequests(requests, extensionsReducer);
        return new ValueOrPromise(() => executor(mergedRequests)).then(resultBatches => {
            if (isAsyncIterable(resultBatches)) {
                throw new Error('Executor must not return incremental results for batching');
            }
            return splitResult(resultBatches, requests.length);
        });
    };
}
function defaultExtensionsReducer(mergedExtensions, request) {
    const newExtensions = request.extensions;
    if (newExtensions != null) {
        Object.assign(mergedExtensions, newExtensions);
    }
    return mergedExtensions;
}
