"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createDefaultExecutor = exports.delegateRequest = exports.delegateToSchema = void 0;
const graphql_1 = require("graphql");
const batch_execute_1 = require("@graphql-tools/batch-execute");
const executor_1 = require("@graphql-tools/executor");
const utils_1 = require("@graphql-tools/utils");
const applySchemaTransforms_js_1 = require("./applySchemaTransforms.js");
const createRequest_js_1 = require("./createRequest.js");
const Subschema_js_1 = require("./Subschema.js");
const subschemaConfig_js_1 = require("./subschemaConfig.js");
const Transformer_js_1 = require("./Transformer.js");
function delegateToSchema(options) {
    const { info, schema, rootValue = schema.rootValue ?? info.rootValue, operationName = info.operation.name?.value, operation = (0, createRequest_js_1.getDelegatingOperation)(info.parentType, info.schema), fieldName = info.fieldName, selectionSet, fieldNodes = info.fieldNodes, context, } = options;
    const request = (0, createRequest_js_1.createRequest)({
        sourceSchema: info.schema,
        sourceParentType: info.parentType,
        sourceFieldName: info.fieldName,
        fragments: info.fragments,
        variableDefinitions: info.operation.variableDefinitions,
        variableValues: info.variableValues,
        targetRootValue: rootValue,
        targetOperationName: operationName,
        targetOperation: operation,
        targetFieldName: fieldName,
        selectionSet,
        fieldNodes,
        context,
        info,
    });
    return delegateRequest({
        ...options,
        request,
    });
}
exports.delegateToSchema = delegateToSchema;
function getDelegationReturnType(targetSchema, operation, fieldName) {
    const rootType = (0, utils_1.getDefinedRootType)(targetSchema, operation);
    const rootFieldType = rootType.getFields()[fieldName];
    if (!rootFieldType) {
        throw new Error(`Unable to find field '${fieldName}' in type '${rootType}'.`);
    }
    return rootFieldType.type;
}
function delegateRequest(options) {
    const delegationContext = getDelegationContext(options);
    const transformer = new Transformer_js_1.Transformer(delegationContext);
    const processedRequest = transformer.transformRequest(options.request);
    if (options.validateRequest) {
        validateRequest(delegationContext, processedRequest.document);
    }
    const executor = getExecutor(delegationContext);
    const result$ = executor(processedRequest);
    function handleExecutorResult(executorResult) {
        if ((0, utils_1.isAsyncIterable)(executorResult)) {
            const iterator = executorResult[Symbol.asyncIterator]();
            // "subscribe" to the subscription result and map the result through the transforms
            return (0, utils_1.mapAsyncIterator)(iterator, result => transformer.transformResult(result));
        }
        return transformer.transformResult(executorResult);
    }
    if ((0, utils_1.isPromise)(result$)) {
        return result$.then(handleExecutorResult);
    }
    return handleExecutorResult(result$);
}
exports.delegateRequest = delegateRequest;
function getDelegationContext({ request, schema, fieldName, returnType, args, info, transforms = [], transformedSchema, skipTypeMerging = false, }) {
    const operationDefinition = (0, utils_1.getOperationASTFromRequest)(request);
    let targetFieldName;
    if (fieldName == null) {
        targetFieldName = operationDefinition.selectionSet.selections[0].name.value;
    }
    else {
        targetFieldName = fieldName;
    }
    const stitchingInfo = info?.schema.extensions?.['stitchingInfo'];
    const subschemaOrSubschemaConfig = stitchingInfo?.subschemaMap.get(schema) ?? schema;
    const operation = operationDefinition.operation;
    if ((0, subschemaConfig_js_1.isSubschemaConfig)(subschemaOrSubschemaConfig)) {
        const targetSchema = subschemaOrSubschemaConfig.schema;
        return {
            subschema: schema,
            subschemaConfig: subschemaOrSubschemaConfig,
            targetSchema,
            operation,
            fieldName: targetFieldName,
            args,
            context: request.context,
            info,
            returnType: returnType ??
                info?.returnType ??
                getDelegationReturnType(targetSchema, operation, targetFieldName),
            transforms: subschemaOrSubschemaConfig.transforms != null
                ? subschemaOrSubschemaConfig.transforms.concat(transforms)
                : transforms,
            transformedSchema: transformedSchema ??
                (subschemaOrSubschemaConfig instanceof Subschema_js_1.Subschema
                    ? subschemaOrSubschemaConfig.transformedSchema
                    : (0, applySchemaTransforms_js_1.applySchemaTransforms)(targetSchema, subschemaOrSubschemaConfig)),
            skipTypeMerging,
        };
    }
    return {
        subschema: schema,
        subschemaConfig: undefined,
        targetSchema: subschemaOrSubschemaConfig,
        operation,
        fieldName: targetFieldName,
        args,
        context: request.context,
        info,
        returnType: returnType ??
            info?.returnType ??
            getDelegationReturnType(subschemaOrSubschemaConfig, operation, targetFieldName),
        transforms,
        transformedSchema: transformedSchema ?? subschemaOrSubschemaConfig,
        skipTypeMerging,
    };
}
function validateRequest(delegationContext, document) {
    const errors = (0, graphql_1.validate)(delegationContext.targetSchema, document);
    if (errors.length > 0) {
        if (errors.length > 1) {
            const combinedError = new AggregateError(errors, errors.map(error => error.message).join(', \n'));
            throw combinedError;
        }
        const error = errors[0];
        throw error.originalError || error;
    }
}
const GLOBAL_CONTEXT = {};
function getExecutor(delegationContext) {
    const { subschemaConfig, targetSchema, context } = delegationContext;
    let executor = subschemaConfig?.executor || (0, exports.createDefaultExecutor)(targetSchema);
    if (subschemaConfig?.batch) {
        const batchingOptions = subschemaConfig?.batchingOptions;
        executor = (0, batch_execute_1.getBatchingExecutor)(context ?? GLOBAL_CONTEXT, executor, batchingOptions?.dataLoaderOptions, batchingOptions?.extensionsReducer);
    }
    return executor;
}
exports.createDefaultExecutor = (0, utils_1.memoize1)(function createDefaultExecutor(schema) {
    return function defaultExecutor(request) {
        return (0, executor_1.normalizedExecutor)({
            schema,
            document: request.document,
            rootValue: request.rootValue,
            contextValue: request.context,
            variableValues: request.variables,
            operationName: request.operationName,
        });
    };
});
