"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.mergeFields = exports.getActualFieldNodes = exports.getUnpathedErrors = exports.getSubschema = exports.annotateExternalObject = exports.isExternalObject = void 0;
const graphql_1 = require("graphql");
const utils_1 = require("@graphql-tools/utils");
const symbols_js_1 = require("./symbols.js");
function isExternalObject(data) {
    return data[symbols_js_1.UNPATHED_ERRORS_SYMBOL] !== undefined;
}
exports.isExternalObject = isExternalObject;
function annotateExternalObject(object, errors, subschema, subschemaMap) {
    Object.defineProperties(object, {
        [symbols_js_1.OBJECT_SUBSCHEMA_SYMBOL]: { value: subschema },
        [symbols_js_1.FIELD_SUBSCHEMA_MAP_SYMBOL]: { value: subschemaMap },
        [symbols_js_1.UNPATHED_ERRORS_SYMBOL]: { value: errors },
    });
    return object;
}
exports.annotateExternalObject = annotateExternalObject;
function getSubschema(object, responseKey) {
    return object[symbols_js_1.FIELD_SUBSCHEMA_MAP_SYMBOL][responseKey] ?? object[symbols_js_1.OBJECT_SUBSCHEMA_SYMBOL];
}
exports.getSubschema = getSubschema;
function getUnpathedErrors(object) {
    return object[symbols_js_1.UNPATHED_ERRORS_SYMBOL];
}
exports.getUnpathedErrors = getUnpathedErrors;
const EMPTY_ARRAY = [];
const EMPTY_OBJECT = Object.create(null);
exports.getActualFieldNodes = (0, utils_1.memoize1)(function (fieldNode) {
    return [fieldNode];
});
function mergeFields(mergedTypeInfo, object, sourceSubschema, context, info) {
    const delegationMaps = mergedTypeInfo.delegationPlanBuilder(info.schema, sourceSubschema, info.variableValues != null && Object.keys(info.variableValues).length > 0
        ? info.variableValues
        : EMPTY_OBJECT, info.fragments != null && Object.keys(info.fragments).length > 0
        ? info.fragments
        : EMPTY_OBJECT, info.fieldNodes?.length
        ? info.fieldNodes.length === 1
            ? (0, exports.getActualFieldNodes)(info.fieldNodes[0])
            : info.fieldNodes
        : EMPTY_ARRAY);
    const res$ = delegationMaps.reduce((prev, delegationMap) => {
        function executeFn() {
            return executeDelegationStage(mergedTypeInfo, delegationMap, object, context, info);
        }
        if ((0, utils_1.isPromise)(prev)) {
            return prev.then(executeFn);
        }
        return executeFn();
    }, undefined);
    if ((0, utils_1.isPromise)(res$)) {
        return res$.then(() => object);
    }
    return object;
}
exports.mergeFields = mergeFields;
function handleResolverResult(resolverResult, subschema, selectionSet, object, combinedFieldSubschemaMap, info, path, combinedErrors) {
    if (resolverResult instanceof Error || resolverResult == null) {
        const schema = subschema.transformedSchema || info.schema;
        const type = schema.getType(object.__typename);
        const { fields } = (0, utils_1.collectFields)(schema, EMPTY_OBJECT, EMPTY_OBJECT, type, selectionSet);
        const nullResult = {};
        for (const [responseKey, fieldNodes] of fields) {
            const combinedPath = [...path, responseKey];
            if (resolverResult instanceof graphql_1.GraphQLError) {
                nullResult[responseKey] = (0, utils_1.relocatedError)(resolverResult, combinedPath);
            }
            else if (resolverResult instanceof Error) {
                nullResult[responseKey] = (0, graphql_1.locatedError)(resolverResult, fieldNodes, combinedPath);
            }
            else {
                nullResult[responseKey] = null;
            }
        }
        resolverResult = nullResult;
    }
    else {
        if (resolverResult[symbols_js_1.UNPATHED_ERRORS_SYMBOL]) {
            combinedErrors.push(...resolverResult[symbols_js_1.UNPATHED_ERRORS_SYMBOL]);
        }
    }
    const objectSubschema = resolverResult[symbols_js_1.OBJECT_SUBSCHEMA_SYMBOL];
    const fieldSubschemaMap = resolverResult[symbols_js_1.FIELD_SUBSCHEMA_MAP_SYMBOL];
    for (const responseKey in resolverResult) {
        if (responseKey === '__proto__') {
            continue;
        }
        const existingPropValue = object[responseKey];
        const sourcePropValue = resolverResult[responseKey];
        if (sourcePropValue != null || existingPropValue == null) {
            object[responseKey] = sourcePropValue;
        }
        combinedFieldSubschemaMap[responseKey] = fieldSubschemaMap?.[responseKey] ?? objectSubschema;
    }
}
function executeDelegationStage(mergedTypeInfo, delegationMap, object, context, info) {
    const combinedErrors = object[symbols_js_1.UNPATHED_ERRORS_SYMBOL];
    const path = (0, graphql_1.responsePathAsArray)(info.path);
    const combinedFieldSubschemaMap = object[symbols_js_1.FIELD_SUBSCHEMA_MAP_SYMBOL];
    const jobs = [];
    for (const [subschema, selectionSet] of delegationMap) {
        const schema = subschema.transformedSchema || info.schema;
        const type = schema.getType(object.__typename);
        const resolver = mergedTypeInfo.resolvers.get(subschema);
        if (resolver) {
            try {
                const resolverResult$ = resolver(object, context, info, subschema, selectionSet, undefined, type);
                if ((0, utils_1.isPromise)(resolverResult$)) {
                    jobs.push(resolverResult$.then(resolverResult => handleResolverResult(resolverResult, subschema, selectionSet, object, combinedFieldSubschemaMap, info, path, combinedErrors)).catch(error => handleResolverResult(error, subschema, selectionSet, object, combinedFieldSubschemaMap, info, path, combinedErrors)));
                }
                else {
                    handleResolverResult(resolverResult$, subschema, selectionSet, object, combinedFieldSubschemaMap, info, path, combinedErrors);
                }
            }
            catch (error) {
                handleResolverResult(error, subschema, selectionSet, object, combinedFieldSubschemaMap, info, path, combinedErrors);
            }
        }
    }
    if (jobs.length) {
        if (jobs.length === 1) {
            return jobs[0];
        }
        return Promise.all(jobs);
    }
}
