"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BelongsToTransformer = void 0;
const graphql_transformer_core_1 = require("@aws-amplify/graphql-transformer-core");
const graphql_transformer_common_1 = require("graphql-transformer-common");
const immer_1 = __importDefault(require("immer"));
const schema_1 = require("./schema");
const utils_1 = require("./utils");
const generator_factory_1 = require("./resolver/generator-factory");
const belongs_to_directive_transformer_factory_1 = require("./belongs-to/belongs-to-directive-transformer-factory");
const directiveName = 'belongsTo';
const directiveDefinition = `
  directive @${directiveName}(fields: [String!], references: [String!]) on FIELD_DEFINITION
`;
class BelongsToTransformer extends graphql_transformer_core_1.TransformerPluginBase {
    constructor() {
        super('amplify-belongs-to-transformer', directiveDefinition);
        this.directiveList = [];
        this.field = (parent, definition, directive, context) => {
            const directiveWrapped = new graphql_transformer_core_1.DirectiveWrapper(directive);
            const args = directiveWrapped.getArguments({
                directiveName,
                object: parent,
                field: definition,
                directive,
            }, (0, graphql_transformer_core_1.generateGetArgumentsInput)(context.transformParameters));
            validate(args, context);
            this.directiveList.push(args);
        };
        this.mutateSchema = (context) => {
            const resultDoc = (0, immer_1.default)(context.inputDocument, (draftDoc) => {
                var _a;
                const objectTypeMap = new Map();
                const filteredDefs = (_a = draftDoc === null || draftDoc === void 0 ? void 0 : draftDoc.definitions) === null || _a === void 0 ? void 0 : _a.filter((def) => def.kind === 'ObjectTypeExtension' || def.kind === 'ObjectTypeDefinition');
                const objectDefs = filteredDefs;
                objectDefs === null || objectDefs === void 0 ? void 0 : objectDefs.forEach((def) => objectTypeMap.set(def.name.value, def));
                objectDefs === null || objectDefs === void 0 ? void 0 : objectDefs.forEach((def) => {
                    var _a;
                    const filteredFields = (_a = def === null || def === void 0 ? void 0 : def.fields) === null || _a === void 0 ? void 0 : _a.filter((field) => { var _a; return (_a = field === null || field === void 0 ? void 0 : field.directives) === null || _a === void 0 ? void 0 : _a.some((dir) => dir.name.value === directiveName && objectTypeMap.get((0, graphql_transformer_common_1.getBaseType)(field.type))); });
                    filteredFields === null || filteredFields === void 0 ? void 0 : filteredFields.forEach((field) => {
                        var _a, _b, _c, _d, _e, _f;
                        const relatedType = objectTypeMap.get((0, graphql_transformer_common_1.getBaseType)(field.type));
                        const relationTypeField = (_a = relatedType === null || relatedType === void 0 ? void 0 : relatedType.fields) === null || _a === void 0 ? void 0 : _a.find((relatedField) => {
                            var _a;
                            return (0, graphql_transformer_common_1.getBaseType)(relatedField.type) === def.name.value &&
                                ((_a = relatedField === null || relatedField === void 0 ? void 0 : relatedField.directives) === null || _a === void 0 ? void 0 : _a.some((relatedDir) => relatedDir.name.value === 'hasOne' || relatedDir.name.value === 'hasMany'));
                        });
                        const relationTypeName = (_d = (_c = (_b = relationTypeField === null || relationTypeField === void 0 ? void 0 : relationTypeField.directives) === null || _b === void 0 ? void 0 : _b.find((relationDir) => relationDir.name.value === 'hasOne' || relationDir.name.value === 'hasMany')) === null || _c === void 0 ? void 0 : _c.name) === null || _d === void 0 ? void 0 : _d.value;
                        if (relationTypeName === 'hasOne') {
                            const connectionAttributeName = (0, utils_1.getConnectionAttributeName)(context.transformParameters, def.name.value, field.name.value, (0, utils_1.getObjectPrimaryKey)(def).name.value);
                            if (!((_e = def === null || def === void 0 ? void 0 : def.fields) === null || _e === void 0 ? void 0 : _e.some((defField) => defField.name.value === connectionAttributeName))) {
                                (_f = def === null || def === void 0 ? void 0 : def.fields) === null || _f === void 0 ? void 0 : _f.push((0, graphql_transformer_common_1.makeField)(connectionAttributeName, [], (0, graphql_transformer_common_1.isNonNullType)(field.type) ? (0, graphql_transformer_common_1.makeNonNullType)((0, graphql_transformer_common_1.makeNamedType)('ID')) : (0, graphql_transformer_common_1.makeNamedType)('ID'), []));
                            }
                        }
                    });
                });
            });
            return resultDoc;
        };
        this.prepare = (context) => {
            this.directiveList.forEach((config) => {
                const modelName = config.object.name.value;
                const dbType = (0, graphql_transformer_core_1.getStrategyDbTypeFromModel)(context, modelName);
                const dataSourceBasedTransformer = (0, belongs_to_directive_transformer_factory_1.getBelongsToDirectiveTransformer)(dbType);
                dataSourceBasedTransformer.prepare(context, config);
            });
        };
        this.transformSchema = (ctx) => {
            const context = ctx;
            for (const config of this.directiveList) {
                const dbType = (0, graphql_transformer_core_1.getStrategyDbTypeFromTypeNode)(config.field.type, context);
                const dataSourceBasedTransformer = (0, belongs_to_directive_transformer_factory_1.getBelongsToDirectiveTransformer)(dbType);
                dataSourceBasedTransformer.transformSchema(ctx, config);
                (0, schema_1.ensureBelongsToConnectionField)(config, context);
            }
        };
        this.generateResolvers = (ctx) => {
            const context = ctx;
            for (const config of this.directiveList) {
                const dbType = (0, graphql_transformer_core_1.getStrategyDbTypeFromTypeNode)(config.field.type, context);
                const generator = (0, generator_factory_1.getGenerator)(dbType);
                generator.makeBelongsToGetItemConnectionWithKeyResolver(config, context);
            }
        };
    }
}
exports.BelongsToTransformer = BelongsToTransformer;
const validate = (config, ctx) => {
    var _a, _b;
    const { field, object } = config;
    let dbType;
    try {
        dbType = (0, graphql_transformer_core_1.getStrategyDbTypeFromTypeNode)(field.type, ctx);
    }
    catch (_c) {
        throw new graphql_transformer_core_1.InvalidDirectiveError(`Object type ${(_b = (_a = field.type) === null || _a === void 0 ? void 0 : _a.name.value) !== null && _b !== void 0 ? _b : field.name} must be annotated with @model.`);
    }
    config.relatedType = (0, utils_1.getRelatedType)(config, ctx);
    const dataSourceBasedTransformer = (0, belongs_to_directive_transformer_factory_1.getBelongsToDirectiveTransformer)(dbType);
    dataSourceBasedTransformer.validate(ctx, config);
    (0, utils_1.validateModelDirective)(config);
    if ((0, graphql_transformer_common_1.isListType)(field.type)) {
        throw new graphql_transformer_core_1.InvalidDirectiveError(`@${directiveName} cannot be used with lists.`);
    }
    config.connectionFields = [];
    (0, utils_1.validateRelatedModelDirective)(config);
    const isBiRelation = config.relatedType.fields.some((relatedField) => {
        if ((0, graphql_transformer_common_1.getBaseType)(relatedField.type) !== object.name.value) {
            return false;
        }
        return relatedField.directives.some((relatedDirective) => {
            if (relatedDirective.name.value === 'hasOne' || relatedDirective.name.value === 'hasMany') {
                config.relatedField = relatedField;
                config.relationType = relatedDirective.name.value;
                return true;
            }
            return false;
        });
    });
    if (!isBiRelation && dbType === graphql_transformer_core_1.DDB_DB_TYPE) {
        throw new graphql_transformer_core_1.InvalidDirectiveError(`${config.relatedType.name.value} must have a relationship with ${object.name.value} in order to use @${directiveName}.`);
    }
};
//# sourceMappingURL=graphql-belongs-to-transformer.js.map