"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.composeResolvers = void 0;
const tslib_1 = require("tslib");
const chain_functions_js_1 = require("./chain-functions.js");
const lodash_1 = tslib_1.__importDefault(require("lodash"));
const utils_1 = require("@graphql-tools/utils");
const micromatch_1 = tslib_1.__importDefault(require("micromatch"));
function isScalarTypeConfiguration(config) {
    return config && 'serialize' in config && 'parseLiteral' in config;
}
function resolveRelevantMappings(resolvers, path) {
    if (!resolvers) {
        return [];
    }
    const [typeNameOrGlob, fieldNameOrGlob] = path.split('.');
    const isTypeMatch = micromatch_1.default.matcher(typeNameOrGlob);
    let fixedFieldGlob = fieldNameOrGlob;
    // convert single value OR `{singleField}` to `singleField` as matching will fail otherwise
    if (fixedFieldGlob.includes('{') && !fixedFieldGlob.includes(',')) {
        fixedFieldGlob = fieldNameOrGlob.replace('{', '').replace('}', '');
    }
    fixedFieldGlob = fixedFieldGlob.replace(', ', ',').trim();
    const isFieldMatch = micromatch_1.default.matcher(fixedFieldGlob);
    const mappings = [];
    for (const typeName in resolvers) {
        if (!isTypeMatch(typeName)) {
            continue;
        }
        if (isScalarTypeConfiguration(resolvers[typeName])) {
            continue;
        }
        const fieldMap = resolvers[typeName];
        if (!fieldMap) {
            return [];
        }
        for (const field in fieldMap) {
            if (!isFieldMatch(field)) {
                continue;
            }
            const resolvedPath = `${typeName}.${field}`;
            if (resolvers[typeName] && resolvers[typeName][field]) {
                if (resolvers[typeName][field].subscribe) {
                    mappings.push(resolvedPath + '.subscribe');
                }
                if (resolvers[typeName][field].resolve) {
                    mappings.push(resolvedPath + '.resolve');
                }
                if (typeof resolvers[typeName][field] === 'function') {
                    mappings.push(resolvedPath);
                }
            }
        }
    }
    return mappings;
}
/**
 * Wraps the resolvers object with the resolvers composition objects.
 * Implemented as a simple and basic middleware mechanism.
 *
 * @param resolvers - resolvers object
 * @param mapping - resolvers composition mapping
 * @hidden
 */
function composeResolvers(resolvers, mapping = {}) {
    const mappingResult = {};
    for (const resolverPath in mapping) {
        const resolverPathMapping = mapping[resolverPath];
        if (resolverPathMapping instanceof Array || typeof resolverPathMapping === 'function') {
            const composeFns = resolverPathMapping;
            const relevantFields = resolveRelevantMappings(resolvers, resolverPath);
            for (const path of relevantFields) {
                mappingResult[path] = (0, utils_1.asArray)(composeFns);
            }
        }
        else if (resolverPathMapping) {
            for (const fieldName in resolverPathMapping) {
                const composeFns = resolverPathMapping[fieldName];
                const relevantFields = resolveRelevantMappings(resolvers, resolverPath + '.' + fieldName);
                for (const path of relevantFields) {
                    mappingResult[path] = (0, utils_1.asArray)(composeFns);
                }
            }
        }
    }
    for (const path in mappingResult) {
        const fns = (0, chain_functions_js_1.chainFunctions)([...(0, utils_1.asArray)(mappingResult[path]), () => lodash_1.default.get(resolvers, path)]);
        lodash_1.default.set(resolvers, path, fns());
    }
    return resolvers;
}
exports.composeResolvers = composeResolvers;
