import { cloneDeep } from './util/cloneDeep';
import { checkDocument, getOperationDefinitionOrDie, getFragmentDefinitions, createFragmentMap, } from './getFromAST';
var TYPENAME_FIELD = {
    kind: 'Field',
    name: {
        kind: 'Name',
        value: '__typename',
    },
};
function isNotEmpty(op, fragments) {
    return (op.selectionSet.selections.filter(function (selectionSet) {
        return !(selectionSet &&
            selectionSet.kind === 'FragmentSpread' &&
            !isNotEmpty(fragments[selectionSet.name.value], fragments));
    }).length > 0);
}
function getDirectiveMatcher(directives) {
    return function directiveMatcher(directive) {
        return directives.some(function (dir) {
            if (dir.name && dir.name === directive.name.value)
                return true;
            if (dir.test && dir.test(directive))
                return true;
            return false;
        });
    };
}
function addTypenameToSelectionSet(selectionSet, isRoot) {
    if (isRoot === void 0) { isRoot = false; }
    if (selectionSet.selections) {
        if (!isRoot) {
            var alreadyHasThisField = selectionSet.selections.some(function (selection) {
                return (selection.kind === 'Field' &&
                    selection.name.value === '__typename');
            });
            if (!alreadyHasThisField) {
                selectionSet.selections.push(TYPENAME_FIELD);
            }
        }
        selectionSet.selections.forEach(function (selection) {
            if (selection.kind === 'Field') {
                if (selection.name.value.lastIndexOf('__', 0) !== 0 &&
                    selection.selectionSet) {
                    addTypenameToSelectionSet(selection.selectionSet);
                }
            }
            else if (selection.kind === 'InlineFragment') {
                if (selection.selectionSet) {
                    addTypenameToSelectionSet(selection.selectionSet);
                }
            }
        });
    }
}
function getSelectionsMatchingDirectiveFromSelectionSet(directives, selectionSet, invert, fieldsOnly) {
    if (invert === void 0) { invert = false; }
    if (fieldsOnly === void 0) { fieldsOnly = false; }
    return selectionSet.selections
        .map(function (selection) {
        if (selection.kind !== 'Field' ||
            !selection ||
            !selection.directives) {
            return fieldsOnly ? null : selection;
        }
        var isMatch;
        var directiveMatcher = getDirectiveMatcher(directives);
        selection.directives = selection.directives.filter(function (directive) {
            var shouldKeep = !directiveMatcher(directive);
            if (!isMatch && !shouldKeep) {
                isMatch = true;
            }
            return shouldKeep;
        });
        return isMatch && invert ? null : selection;
    })
        .filter(function (s) { return !!s; });
}
function removeDirectivesFromSelectionSet(directives, selectionSet) {
    if (!selectionSet.selections)
        return selectionSet;
    var agressiveRemove = directives.some(function (dir) { return dir.remove; });
    selectionSet.selections = getSelectionsMatchingDirectiveFromSelectionSet(directives, selectionSet, agressiveRemove);
    selectionSet.selections.forEach(function (selection) {
        if ((selection.kind === 'Field' || selection.kind === 'InlineFragment') &&
            selection.selectionSet) {
            removeDirectivesFromSelectionSet(directives, selection.selectionSet);
        }
    });
    return selectionSet;
}
export function removeDirectivesFromDocument(directives, doc) {
    var docClone = cloneDeep(doc);
    var removedArguments = [];
    var removedFragments = [];
    var aggressiveRemove = directives.some(function (directive) { return directive.remove; });
    docClone.definitions.forEach(function (definition) {
        var operationDefinition = definition;
        var originalSelectionSet = cloneDeep(operationDefinition.selectionSet);
        var newSelectionSet = removeDirectivesFromSelectionSet(directives, operationDefinition.selectionSet);
        if (aggressiveRemove && !!docClone) {
            var matchingSelections = getSelectionsMatchingDirectiveFromSelectionSet(directives.map(function (config) { return ({
                name: config.name,
                test: config.test,
            }); }), originalSelectionSet);
            var remainingArguments_1 = getAllArgumentsFromSelectionSet(newSelectionSet);
            removedArguments = removedArguments.concat(matchingSelections
                .map(getAllArgumentsFromSelection)
                .reduce(function (allArguments, selectionArguments) { return allArguments.concat(selectionArguments); }, [])
                .filter(function (removedArg) {
                return !remainingArguments_1.some(function (remainingArg) {
                    if (remainingArg.value.kind !== 'Variable' ||
                        !remainingArg.value)
                        return false;
                    if (removedArg.value.kind !== 'Variable' ||
                        !removedArg.value)
                        return false;
                    return (remainingArg.value.name.value === removedArg.value.name.value);
                });
            })
                .map(function (argument) {
                if (argument.value.kind !== 'Variable' ||
                    !argument.value)
                    return null;
                return {
                    name: argument.value.name.value,
                    remove: aggressiveRemove,
                };
            })
                .filter(function (node) { return !!node; }));
            var remainingFragmentSpreads_1 = getAllFragmentSpreadsFromSelectionSet(newSelectionSet);
            removedFragments = removedFragments.concat(matchingSelections
                .map(getAllFragmentSpreadsFromSelection)
                .reduce(function (allFragments, selectionFragments) { return allFragments.concat(selectionFragments); }, [])
                .filter(function (removedFragment) {
                return !remainingFragmentSpreads_1.some(function (remainingFragment) {
                    return remainingFragment.name.value === removedFragment.name.value;
                });
            })
                .map(function (fragment) { return ({
                name: fragment.name.value,
                remove: aggressiveRemove,
            }); }));
        }
    });
    if (!docClone) {
        return null;
    }
    if (removedFragments.length > 0) {
        docClone = removeFragmentSpreadFromDocument(removedFragments, docClone);
        if (!docClone) {
            return null;
        }
    }
    if (removedArguments.length > 0) {
        docClone = removeArgumentsFromDocument(removedArguments, docClone);
        if (!docClone) {
            return null;
        }
    }
    var operation = getOperationDefinitionOrDie(docClone);
    var fragments = createFragmentMap(getFragmentDefinitions(docClone));
    return isNotEmpty(operation, fragments) ? docClone : null;
}
export function addTypenameToDocument(doc) {
    checkDocument(doc);
    var docClone = cloneDeep(doc);
    docClone.definitions.forEach(function (definition) {
        var isRoot = definition.kind === 'OperationDefinition';
        addTypenameToSelectionSet(definition.selectionSet, isRoot);
    });
    return docClone;
}
var connectionRemoveConfig = {
    test: function (directive) {
        var willRemove = directive.name.value === 'connection';
        if (willRemove) {
            if (!directive.arguments ||
                !directive.arguments.some(function (arg) { return arg.name.value === 'key'; })) {
                console.warn('Removing an @connection directive even though it does not have a key. ' +
                    'You may want to use the key parameter to specify a store key.');
            }
        }
        return willRemove;
    },
};
export function removeConnectionDirectiveFromDocument(doc) {
    checkDocument(doc);
    return removeDirectivesFromDocument([connectionRemoveConfig], doc);
}
function hasDirectivesInSelectionSet(directives, selectionSet, nestedCheck) {
    if (nestedCheck === void 0) { nestedCheck = true; }
    return filterSelectionSet(selectionSet, function (selection) {
        return hasDirectivesInSelection(directives, selection, nestedCheck);
    });
}
function hasDirectivesInSelection(directives, selection, nestedCheck) {
    if (nestedCheck === void 0) { nestedCheck = true; }
    if (selection.kind !== 'Field' || !selection) {
        return true;
    }
    if (!selection.directives) {
        return false;
    }
    var directiveMatcher = getDirectiveMatcher(directives);
    var matchedDirectives = selection.directives.filter(directiveMatcher);
    var hasMatches = matchedDirectives.length > 0;
    return (hasMatches ||
        (nestedCheck &&
            hasDirectivesInSelectionSet(directives, selection.selectionSet, nestedCheck)));
}
function getDirectivesFromSelectionSet(directives, selectionSet) {
    selectionSet.selections = selectionSet.selections
        .filter(function (selection) {
        return hasDirectivesInSelection(directives, selection, true);
    })
        .map(function (selection) {
        if (hasDirectivesInSelection(directives, selection, false)) {
            return selection;
        }
        if ((selection.kind === 'Field' || selection.kind === 'InlineFragment') &&
            selection.selectionSet) {
            selection.selectionSet = getDirectivesFromSelectionSet(directives, selection.selectionSet);
        }
        return selection;
    });
    return selectionSet;
}
export function getDirectivesFromDocument(directives, doc, includeAllFragments) {
    if (includeAllFragments === void 0) { includeAllFragments = false; }
    checkDocument(doc);
    var docClone = cloneDeep(doc);
    docClone.definitions = docClone.definitions.map(function (definition) {
        if ((definition.kind === 'OperationDefinition' ||
            (definition.kind === 'FragmentDefinition' && !includeAllFragments)) &&
            definition.selectionSet) {
            definition.selectionSet = getDirectivesFromSelectionSet(directives, definition.selectionSet);
        }
        return definition;
    });
    var operation = getOperationDefinitionOrDie(docClone);
    var fragments = createFragmentMap(getFragmentDefinitions(docClone));
    return isNotEmpty(operation, fragments) ? docClone : null;
}
function getArgumentMatcher(config) {
    return function (argument) {
        return config.some(function (aConfig) {
            if (argument.value.kind !== 'Variable' ||
                !argument.value)
                return false;
            if (!argument.value.name)
                return false;
            if (aConfig.name === argument.value.name.value)
                return true;
            if (aConfig.test && aConfig.test(argument))
                return true;
            return false;
        });
    };
}
function hasArgumentsInSelectionSet(config, selectionSet, nestedCheck) {
    if (nestedCheck === void 0) { nestedCheck = false; }
    return filterSelectionSet(selectionSet, function (selection) {
        return hasArgumentsInSelection(config, selection, nestedCheck);
    });
}
function hasArgumentsInSelection(config, selection, nestedCheck) {
    if (nestedCheck === void 0) { nestedCheck = false; }
    if (selection.kind !== 'Field' || !selection) {
        return true;
    }
    if (!selection.arguments) {
        return false;
    }
    var matcher = getArgumentMatcher(config);
    var matchedArguments = selection.arguments.filter(matcher);
    return (matchedArguments.length > 0 ||
        (nestedCheck &&
            hasArgumentsInSelectionSet(config, selection.selectionSet, nestedCheck)));
}
function getAllArgumentsFromSelectionSet(selectionSet) {
    return selectionSet.selections
        .map(getAllArgumentsFromSelection)
        .reduce(function (allArguments, selectionArguments) {
        return allArguments.concat(selectionArguments);
    }, []);
}
function getAllArgumentsFromSelection(selection) {
    if (selection.kind !== 'Field' || !selection) {
        return [];
    }
    return selection.arguments || [];
}
export function removeArgumentsFromDocument(config, query) {
    var docClone = cloneDeep(query);
    docClone.definitions.forEach(function (definition) {
        var operationDefinition = definition;
        var removeVariableConfig = config
            .filter(function (aConfig) { return !!aConfig.name; })
            .map(function (aConfig) { return ({
            name: aConfig.name,
            remove: aConfig.remove,
        }); });
        removeArgumentsFromSelectionSet(config, operationDefinition.selectionSet);
        removeArgumentsFromOperationDefinition(removeVariableConfig, operationDefinition);
    });
    var operation = getOperationDefinitionOrDie(docClone);
    var fragments = createFragmentMap(getFragmentDefinitions(docClone));
    return isNotEmpty(operation, fragments) ? docClone : null;
}
function removeArgumentsFromOperationDefinition(config, definition) {
    if (!definition.variableDefinitions)
        return definition;
    var aggressiveRemove = config.some(function (aConfig) { return aConfig.remove; });
    var remove;
    definition.variableDefinitions = definition.variableDefinitions.filter(function (aDefinition) {
        var shouldKeep = !config.some(function (aConfig) {
            if (aConfig.name === aDefinition.variable.name.value)
                return true;
            if (aConfig.test && aConfig.test(aDefinition))
                return true;
            return false;
        });
        if (!remove && !shouldKeep && aggressiveRemove) {
            remove = true;
        }
        return shouldKeep;
    });
    return definition;
}
function removeArgumentsFromSelectionSet(config, selectionSet) {
    if (!selectionSet.selections)
        return selectionSet;
    var aggressiveRemove = config.some(function (aConfig) { return aConfig.remove; });
    selectionSet.selections = selectionSet.selections
        .map(function (selection) {
        if (selection.kind !== 'Field' ||
            !selection ||
            !selection.arguments) {
            return selection;
        }
        var remove;
        var argumentMatcher = getArgumentMatcher(config);
        selection.arguments = selection.arguments.filter(function (argument) {
            var shouldKeep = !argumentMatcher(argument);
            if (!remove && !shouldKeep && aggressiveRemove) {
                remove = true;
            }
            return shouldKeep;
        });
        return remove ? null : selection;
    })
        .filter(function (x) { return !!x; });
    selectionSet.selections.forEach(function (selection) {
        if ((selection.kind === 'Field' || selection.kind === 'InlineFragment') &&
            selection.selectionSet) {
            removeArgumentsFromSelectionSet(config, selection.selectionSet);
        }
    });
    return selectionSet;
}
function hasFragmentSpreadInSelection(config, selection) {
    if (selection.kind !== 'FragmentSpread' ||
        !selection) {
        return false;
    }
    return config.some(function (aConfig) {
        if (aConfig.name === selection.name.value)
            return true;
        if (aConfig.test && aConfig.test(selection))
            return true;
        return false;
    });
}
export function removeFragmentSpreadFromDocument(config, query) {
    var docClone = cloneDeep(query);
    docClone.definitions.forEach(function (definition) {
        removeFragmentSpreadFromSelectionSet(config, definition.selectionSet);
    });
    docClone.definitions = removeFragmentSpreadFromDefinitions(config
        .filter(function (aConfig) { return !!aConfig.name; })
        .map(function (aConfig) { return ({ name: aConfig.name }); }), docClone.definitions);
    var operation = getOperationDefinitionOrDie(docClone);
    var fragments = createFragmentMap(getFragmentDefinitions(docClone));
    return isNotEmpty(operation, fragments) ? docClone : null;
}
function removeFragmentSpreadFromDefinitions(config, definitions) {
    return definitions.filter(function (definition) {
        if (definition.kind !== 'FragmentDefinition' ||
            !definition) {
            return true;
        }
        return !config.some(function (aConfig) {
            if (aConfig.name && aConfig.name === definition.name.value)
                return true;
            if (aConfig.test && aConfig.test(definition))
                return true;
            return false;
        });
    });
}
function removeFragmentSpreadFromSelectionSet(config, selectionSet) {
    if (!selectionSet.selections)
        return selectionSet;
    selectionSet.selections = selectionSet.selections.filter(function (selection) { return !hasFragmentSpreadInSelection(config, selection); });
    selectionSet.selections.forEach(function (selection) {
        if ((selection.kind === 'Field' || selection.kind === 'InlineFragment') &&
            selection.selectionSet) {
            removeFragmentSpreadFromSelectionSet(config, selection.selectionSet);
        }
    });
    return selectionSet;
}
function getAllFragmentSpreadsFromSelectionSet(selectionSet) {
    return selectionSet.selections
        .map(getAllFragmentSpreadsFromSelection)
        .reduce(function (allFragments, selectionFragments) { return allFragments.concat(selectionFragments); }, []);
}
function getAllFragmentSpreadsFromSelection(selection) {
    if ((selection.kind === 'Field' || selection.kind === 'InlineFragment') &&
        selection.selectionSet) {
        return getAllFragmentSpreadsFromSelectionSet(selection.selectionSet);
    }
    else if (selection.kind === 'FragmentSpread' &&
        selection) {
        return [selection];
    }
    return [];
}
function filterSelectionSet(selectionSet, filter) {
    if (!(selectionSet && selectionSet.selections)) {
        return false;
    }
    return selectionSet.selections.filter(filter).length > 0;
}
//# sourceMappingURL=transform.js.map