import { newInvariantError } from "@apollo/client/utilities/invariant";
import { canonicalStringify } from "../common/canonicalStringify.js";
import { isNonNullObject } from "../common/objects.js";
import { getFragmentFromSelection } from "./fragments.js";
export function makeReference(id) {
    return { __ref: String(id) };
}
export function isReference(obj) {
    return Boolean(obj && typeof obj === "object" && typeof obj.__ref === "string");
}
export function isDocumentNode(value) {
    return (isNonNullObject(value) &&
        value.kind === "Document" &&
        Array.isArray(value.definitions));
}
function isStringValue(value) {
    return value.kind === "StringValue";
}
function isBooleanValue(value) {
    return value.kind === "BooleanValue";
}
function isIntValue(value) {
    return value.kind === "IntValue";
}
function isFloatValue(value) {
    return value.kind === "FloatValue";
}
function isVariable(value) {
    return value.kind === "Variable";
}
function isObjectValue(value) {
    return value.kind === "ObjectValue";
}
function isListValue(value) {
    return value.kind === "ListValue";
}
function isEnumValue(value) {
    return value.kind === "EnumValue";
}
function isNullValue(value) {
    return value.kind === "NullValue";
}
export function valueToObjectRepresentation(argObj, name, value, variables) {
    if (isIntValue(value) || isFloatValue(value)) {
        argObj[name.value] = Number(value.value);
    }
    else if (isBooleanValue(value) || isStringValue(value)) {
        argObj[name.value] = value.value;
    }
    else if (isObjectValue(value)) {
        const nestedArgObj = {};
        value.fields.map((obj) => valueToObjectRepresentation(nestedArgObj, obj.name, obj.value, variables));
        argObj[name.value] = nestedArgObj;
    }
    else if (isVariable(value)) {
        const variableValue = (variables || {})[value.name.value];
        argObj[name.value] = variableValue;
    }
    else if (isListValue(value)) {
        argObj[name.value] = value.values.map((listValue) => {
            const nestedArgArrayObj = {};
            valueToObjectRepresentation(nestedArgArrayObj, name, listValue, variables);
            return nestedArgArrayObj[name.value];
        });
    }
    else if (isEnumValue(value)) {
        argObj[name.value] = value.value;
    }
    else if (isNullValue(value)) {
        argObj[name.value] = null;
    }
    else {
        throw newInvariantError(21, name.value, value.kind);
    }
}
export function storeKeyNameFromField(field, variables) {
    let directivesObj = null;
    if (field.directives) {
        directivesObj = {};
        field.directives.forEach((directive) => {
            directivesObj[directive.name.value] = {};
            if (directive.arguments) {
                directive.arguments.forEach(({ name, value }) => valueToObjectRepresentation(directivesObj[directive.name.value], name, value, variables));
            }
        });
    }
    let argObj = null;
    if (field.arguments && field.arguments.length) {
        argObj = {};
        field.arguments.forEach(({ name, value }) => valueToObjectRepresentation(argObj, name, value, variables));
    }
    return getStoreKeyName(field.name.value, argObj, directivesObj);
}
const KNOWN_DIRECTIVES = [
    "connection",
    "include",
    "skip",
    "client",
    "rest",
    "export",
    "nonreactive",
];
// Default stable JSON.stringify implementation used by getStoreKeyName. Can be
// updated/replaced with something better by calling
// getStoreKeyName.setStringify(newStringifyFunction).
let storeKeyNameStringify = canonicalStringify;
export const getStoreKeyName = Object.assign(function (fieldName, args, directives) {
    if (args &&
        directives &&
        directives["connection"] &&
        directives["connection"]["key"]) {
        if (directives["connection"]["filter"] &&
            directives["connection"]["filter"].length > 0) {
            const filterKeys = directives["connection"]["filter"] ?
                directives["connection"]["filter"]
                : [];
            filterKeys.sort();
            const filteredArgs = {};
            filterKeys.forEach((key) => {
                filteredArgs[key] = args[key];
            });
            return `${directives["connection"]["key"]}(${storeKeyNameStringify(filteredArgs)})`;
        }
        else {
            return directives["connection"]["key"];
        }
    }
    let completeFieldName = fieldName;
    if (args) {
        // We can't use `JSON.stringify` here since it's non-deterministic,
        // and can lead to different store key names being created even though
        // the `args` object used during creation has the same properties/values.
        const stringifiedArgs = storeKeyNameStringify(args);
        completeFieldName += `(${stringifiedArgs})`;
    }
    if (directives) {
        Object.keys(directives).forEach((key) => {
            if (KNOWN_DIRECTIVES.indexOf(key) !== -1)
                return;
            if (directives[key] && Object.keys(directives[key]).length) {
                completeFieldName += `@${key}(${storeKeyNameStringify(directives[key])})`;
            }
            else {
                completeFieldName += `@${key}`;
            }
        });
    }
    return completeFieldName;
}, {
    setStringify(s) {
        const previous = storeKeyNameStringify;
        storeKeyNameStringify = s;
        return previous;
    },
});
export function argumentsObjectFromField(field, variables) {
    if (field.arguments && field.arguments.length) {
        const argObj = {};
        field.arguments.forEach(({ name, value }) => valueToObjectRepresentation(argObj, name, value, variables));
        return argObj;
    }
    return null;
}
export function resultKeyNameFromField(field) {
    return field.alias ? field.alias.value : field.name.value;
}
export function getTypenameFromResult(result, selectionSet, fragmentMap) {
    let fragments;
    for (const selection of selectionSet.selections) {
        if (isField(selection)) {
            if (selection.name.value === "__typename") {
                return result[resultKeyNameFromField(selection)];
            }
        }
        else if (fragments) {
            fragments.push(selection);
        }
        else {
            fragments = [selection];
        }
    }
    if (typeof result.__typename === "string") {
        return result.__typename;
    }
    if (fragments) {
        for (const selection of fragments) {
            const typename = getTypenameFromResult(result, getFragmentFromSelection(selection, fragmentMap).selectionSet, fragmentMap);
            if (typeof typename === "string") {
                return typename;
            }
        }
    }
}
export function isField(selection) {
    return selection.kind === "Field";
}
export function isInlineFragment(selection) {
    return selection.kind === "InlineFragment";
}
//# sourceMappingURL=storeUtils.js.map
