"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.processTools = exports.isCustomQueryToolPredicate = exports.isModelOperationToolPredicate = void 0;
const graphql_transformer_core_1 = require("@aws-amplify/graphql-transformer-core");
const graphql_transformer_common_1 = require("graphql-transformer-common");
const graphql_json_schema_type_1 = require("./graphql-json-schema-type");
const isModelOperationToolPredicate = (tool) => tool.modelName !== undefined && tool.modelOperation !== undefined && tool.queryName === undefined;
exports.isModelOperationToolPredicate = isModelOperationToolPredicate;
const isCustomQueryToolPredicate = (tool) => tool.queryName !== undefined && tool.modelName === undefined && tool.modelOperation === undefined;
exports.isCustomQueryToolPredicate = isCustomQueryToolPredicate;
const processTools = (toolDefinitions, ctx) => {
    if (!toolDefinitions || toolDefinitions.length === 0) {
        return undefined;
    }
    const queryType = ctx.output.getType('Query');
    if (!queryType.fields || queryType.fields.length === 0) {
        throw new graphql_transformer_core_1.InvalidDirectiveError('Tools must be queries - no queries found in the schema');
    }
    const tools = toolDefinitions.map((toolDefinition) => createTool({ toolDefinition, queryType, ctx }));
    return tools;
};
exports.processTools = processTools;
const generateSelectionSet = (currentType, ctx, seenTypes = new Set(), fieldName = '') => {
    if ((0, graphql_transformer_common_1.isScalar)(currentType)) {
        return fieldName;
    }
    const typeName = (0, graphql_transformer_common_1.getBaseType)(currentType);
    if (seenTypes.has(typeName)) {
        return '';
    }
    const type = getObjectTypeFromName(typeName, ctx);
    seenTypes.add(type.name.value);
    if (!type.fields || type.fields.length === 0) {
        return '';
    }
    const fieldSelections = type.fields
        .map((field) => {
        if ((0, graphql_transformer_common_1.isScalar)(field.type)) {
            return field.name.value;
        }
        else {
            const nestedSelection = generateSelectionSet(field.type, ctx, new Set(seenTypes), field.name.value);
            return nestedSelection ? `${field.name.value} { ${nestedSelection} }` : '';
        }
    })
        .filter(Boolean);
    return fieldSelections.join(' ');
};
const getObjectTypeFromName = (name, ctx) => {
    const node = ctx.output.getObject(name);
    if (!node) {
        throw new Error(`Could not find type definition for ${name}`);
    }
    return node;
};
const createTool = (input) => {
    var _a;
    const { toolDefinition, queryType, ctx } = input;
    const queryName = (0, exports.isModelOperationToolPredicate)(toolDefinition) ? modelListQueryName(toolDefinition, ctx) : toolDefinition.queryName;
    const queryField = (_a = queryType.fields) === null || _a === void 0 ? void 0 : _a.find((field) => field.name.value === queryName);
    if (!queryField) {
        throw new graphql_transformer_core_1.InvalidDirectiveError(`Tool "${toolDefinition.name}" defined in @conversation directive has no matching Query field definition`);
    }
    const { name: toolName, description } = toolDefinition;
    const { type: returnType, arguments: queryFieldArguments } = queryField;
    const fieldArguments = (0, exports.isModelOperationToolPredicate)(toolDefinition)
        ? queryFieldArguments === null || queryFieldArguments === void 0 ? void 0 : queryFieldArguments.filter((arg) => arg.name.value !== 'limit' && arg.name.value !== 'nextToken')
        : queryFieldArguments;
    const toolSchema = generateToolProperties(fieldArguments, ctx);
    const selectionSet = generateSelectionSet(returnType, ctx).trim();
    const propertyTypes = generatePropertyTypes(fieldArguments);
    const graphqlRequestInputDescriptor = {
        selectionSet,
        propertyTypes,
        queryName: queryField.name.value,
    };
    return {
        name: toolName,
        description,
        inputSchema: {
            json: {
                type: 'object',
                ...toolSchema,
            },
        },
        graphqlRequestInputDescriptor,
    };
};
const generateToolProperties = (fieldArguments, ctx) => {
    if (!fieldArguments || fieldArguments.length === 0) {
        return { properties: {}, required: [] };
    }
    const filterSchema = fieldArguments.reduce((acc, fieldArgument) => {
        const fieldArgumentSchema = (0, graphql_json_schema_type_1.generateJSONSchemaFromTypeNode)(fieldArgument.type, ctx);
        if (!fieldArgumentSchema)
            return acc;
        if (fieldArgumentSchema.$defs) {
            acc.$defs = { ...acc.$defs, ...fieldArgumentSchema.$defs };
            delete fieldArgumentSchema.$defs;
        }
        acc.properties = acc.properties || {};
        acc.properties[fieldArgument.name.value] = fieldArgumentSchema;
        if ((0, graphql_transformer_common_1.isNonNullType)(fieldArgument.type)) {
            acc.required = acc.required || [];
            acc.required.push(fieldArgument.name.value);
        }
        return acc;
    }, {});
    return filterSchema;
};
const generatePropertyTypes = (fieldArguments) => {
    if (!fieldArguments || fieldArguments.length === 0) {
        return {};
    }
    return fieldArguments.reduce((acc, fieldArgument) => {
        const name = fieldArgument.name.value;
        const suffix = (0, graphql_transformer_common_1.isNonNullType)(fieldArgument.type) ? '!' : '';
        acc[name] = (0, graphql_transformer_common_1.getBaseType)(fieldArgument.type) + suffix;
        return acc;
    }, {});
};
const modelListQueryName = (modelTool, ctx) => {
    var _a, _b, _c;
    const { modelName } = modelTool;
    const model = getObjectTypeFromName(modelName, ctx);
    const modelDirective = (_a = model.directives) === null || _a === void 0 ? void 0 : _a.find((directive) => directive.name.value === 'model');
    const queriesArgument = (_b = modelDirective === null || modelDirective === void 0 ? void 0 : modelDirective.arguments) === null || _b === void 0 ? void 0 : _b.find((arg) => arg.name.value === 'queries');
    const queriesValue = queriesArgument === null || queriesArgument === void 0 ? void 0 : queriesArgument.value;
    const listField = (_c = queriesValue === null || queriesValue === void 0 ? void 0 : queriesValue.fields) === null || _c === void 0 ? void 0 : _c.find((field) => field.name.value === 'list');
    const listValue = listField === null || listField === void 0 ? void 0 : listField.value;
    const listQueryArgument = listValue === null || listValue === void 0 ? void 0 : listValue.value;
    return listQueryArgument !== null && listQueryArgument !== void 0 ? listQueryArgument : (0, graphql_transformer_core_1.getFieldNameFor)('list', modelName);
};
//# sourceMappingURL=process-tools.js.map