"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GraphQLFactory = void 0;
const tslib_1 = require("tslib");
/* eslint-disable @typescript-eslint/no-unused-expressions */
const schema_1 = require("@graphql-tools/schema");
const common_1 = require("@nestjs/common");
const fs_1 = require("fs");
const graphql_1 = require("graphql");
const graphql_tag_1 = require("graphql-tag");
const lodash_1 = require("lodash");
const graphql_ast_explorer_1 = require("./graphql-ast.explorer");
const graphql_schema_builder_1 = require("./graphql-schema.builder");
const services_1 = require("./services");
const utils_1 = require("./utils");
let GraphQLFactory = class GraphQLFactory {
    constructor(resolversExplorerService, scalarsExplorerService, graphqlAstExplorer, gqlSchemaBuilder) {
        this.resolversExplorerService = resolversExplorerService;
        this.scalarsExplorerService = scalarsExplorerService;
        this.graphqlAstExplorer = graphqlAstExplorer;
        this.gqlSchemaBuilder = gqlSchemaBuilder;
    }
    async generateSchema(options = { typeDefs: [] }) {
        const resolvers = this.resolversExplorerService.explore();
        const rawResolvers = (0, utils_1.extend)(this.scalarsExplorerService.explore(), resolvers);
        const typesResolvers = options.transformResolvers
            ? await options.transformResolvers(rawResolvers)
            : rawResolvers;
        const transformSchema = async (schema) => options.transformSchema ? await options.transformSchema(schema) : schema;
        if (options.autoSchemaFile) {
            const autoGeneratedSchema = await this.gqlSchemaBuilder.build(options.autoSchemaFile, options, this.resolversExplorerService.getAllCtors());
            const executableSchema = (0, schema_1.makeExecutableSchema)({
                resolvers: (0, utils_1.extend)(typesResolvers, options.resolvers),
                typeDefs: (0, graphql_tag_1.gql) `
          ${(0, graphql_1.printSchema)(autoGeneratedSchema)}
        `,
                resolverValidationOptions: {
                    ...(options.resolverValidationOptions || {}),
                    requireResolversForResolveType: 'ignore',
                },
                inheritResolversFromInterfaces: options.inheritResolversFromInterfaces,
            });
            let schema = options.schema
                ? (0, schema_1.mergeSchemas)({
                    schemas: [options.schema, executableSchema],
                })
                : executableSchema;
            const autoGeneratedSchemaConfig = autoGeneratedSchema.toConfig();
            const schemaConfig = this.overrideOrExtendResolvers(schema.toConfig(), autoGeneratedSchemaConfig);
            schema = new graphql_1.GraphQLSchema(schemaConfig);
            schema = await transformSchema(schema);
            schema = options.sortSchema ? (0, graphql_1.lexicographicSortSchema)(schema) : schema;
            return schema;
        }
        if ((0, lodash_1.isEmpty)(options.typeDefs)) {
            const schema = await transformSchema(options.schema);
            return schema;
        }
        const executableSchema = (0, schema_1.makeExecutableSchema)({
            resolvers: (0, utils_1.extend)(typesResolvers, options.resolvers),
            typeDefs: (0, graphql_tag_1.gql) `
        ${options.typeDefs}
      `,
            resolverValidationOptions: options.resolverValidationOptions,
            inheritResolversFromInterfaces: options.inheritResolversFromInterfaces,
        });
        let schema = options.schema
            ? (0, schema_1.mergeSchemas)({
                schemas: [options.schema, executableSchema],
            })
            : executableSchema;
        (0, utils_1.removeTempField)(schema);
        schema = await transformSchema(schema);
        schema = options.sortSchema ? (0, graphql_1.lexicographicSortSchema)(schema) : schema;
        return schema;
    }
    overrideOrExtendResolvers(executableSchemaConfig, autoGeneratedSchemaConfig) {
        const schemaConfig = autoGeneratedSchemaConfig;
        const rootResolverKeys = [
            'mutation',
            'query',
            'subscription',
        ];
        rootResolverKeys
            .filter((key) => executableSchemaConfig[key] && schemaConfig[key])
            .forEach((key) => {
            const executableSchemaFields = executableSchemaConfig[key].getFields();
            const schemaFields = schemaConfig[key].getFields();
            (0, lodash_1.forEach)(executableSchemaFields, (value, resolverName) => {
                if (schemaFields[resolverName]) {
                    schemaFields[resolverName].resolve =
                        executableSchemaFields[resolverName].resolve;
                    schemaFields[resolverName].subscribe =
                        executableSchemaFields[resolverName].subscribe;
                }
                else {
                    schemaFields[resolverName] = executableSchemaFields[resolverName];
                }
            });
        });
        const getAutoGeneratedTypeByName = (name) => schemaConfig.types.find((type) => type.name === name);
        executableSchemaConfig.types
            .filter((type) => type instanceof graphql_1.GraphQLObjectType)
            .forEach((type) => {
            const fields = type.getFields();
            const autoGeneratedType = getAutoGeneratedTypeByName(type.name);
            if (!autoGeneratedType) {
                return;
            }
            /**
             * Inherit "resolve()" functions from auto-generated interfaces
             */
            const implementedInterfaces = autoGeneratedType.getInterfaces() || [];
            if (implementedInterfaces.length > 0) {
                implementedInterfaces.forEach((interfaceRef) => {
                    const interfaceInExecutableSchema = executableSchemaConfig.types.find((type) => type.name === interfaceRef.name);
                    (0, lodash_1.forEach)(interfaceRef.getFields(), (value, key) => {
                        const fieldInExecutableSchema = interfaceInExecutableSchema.getFields()[key];
                        if (!fieldInExecutableSchema) {
                            return;
                        }
                        if (!fieldInExecutableSchema.resolve) {
                            return;
                        }
                        const baseClassField = autoGeneratedType.getFields()[key];
                        baseClassField &&
                            (baseClassField.resolve = fieldInExecutableSchema.resolve);
                    });
                });
            }
            (0, lodash_1.forEach)(fields, (value, key) => {
                if (!value.resolve) {
                    return;
                }
                const field = autoGeneratedType.getFields()[key];
                field && (field.resolve = value.resolve);
            });
        });
        return schemaConfig;
    }
    async generateDefinitions(typeDefs, options) {
        if ((0, lodash_1.isEmpty)(typeDefs) || !options.definitions) {
            return;
        }
        const definitionsGeneratorOptions = {
            emitTypenameField: options.definitions.emitTypenameField,
            skipResolverArgs: options.definitions.skipResolverArgs,
            defaultScalarType: options.definitions.defaultScalarType,
            customScalarTypeMapping: options.definitions.customScalarTypeMapping,
            additionalHeader: options.definitions.additionalHeader,
            defaultTypeMapping: options.definitions.defaultTypeMapping,
            enumsAsTypes: options.definitions.enumsAsTypes,
        };
        const tsFile = await this.graphqlAstExplorer.explore((0, graphql_tag_1.gql) `
        ${typeDefs}
      `, options.definitions.path, options.definitions.outputAs, definitionsGeneratorOptions);
        if (!(0, fs_1.existsSync)(options.definitions.path) ||
            !(0, fs_1.lstatSync)(options.definitions.path).isFile() ||
            (0, fs_1.readFileSync)(options.definitions.path, 'utf8') !== tsFile.getFullText()) {
            await tsFile.save();
        }
    }
};
exports.GraphQLFactory = GraphQLFactory;
exports.GraphQLFactory = GraphQLFactory = tslib_1.__decorate([
    (0, common_1.Injectable)(),
    tslib_1.__metadata("design:paramtypes", [services_1.ResolversExplorerService,
        services_1.ScalarsExplorerService,
        graphql_ast_explorer_1.GraphQLAstExplorer,
        graphql_schema_builder_1.GraphQLSchemaBuilder])
], GraphQLFactory);
