"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.generator = exports.generatorId = void 0;
const Generator = __importStar(require("@postxl/generator"));
const utils_1 = require("@postxl/utils");
const backend_actions_1 = require("../backend-actions");
const backend_core_1 = require("../backend-core");
const backend_database_prisma_1 = require("../backend-database-prisma");
const base_1 = require("../base");
const decoders_1 = require("../decoders");
const seed_data_1 = require("../seed-data");
const types_1 = require("../types");
const bulk_mutation_types_generator_1 = require("./generators/bulk-mutation-types.generator");
const data_service_generator_1 = require("./generators/data-service.generator");
const model_repository_generator_1 = require("./generators/model-repository.generator");
const repository_interface_generator_1 = require("./generators/repository-interface.generator");
const repository_module_generator_1 = require("./generators/repository-module.generator");
const model_types_1 = require("./model.types");
exports.generatorId = Generator.toGeneratorInterfaceId('backend-repositories');
exports.generator = {
    id: exports.generatorId,
    requires: [
        backend_core_1.backendGeneratorId,
        base_1.baseGeneratorId,
        types_1.typesGeneratorId,
        backend_database_prisma_1.databaseGeneratorId,
        decoders_1.decodersGeneratorId,
        backend_actions_1.backendActionsGeneratorId,
        seed_data_1.seedDataGeneratorId,
    ],
    register: (context) => {
        context.backend.packageJson.dependencies.push({
            packageName: Generator.toPackageName('async-mutex'),
            version: '^0.5.0',
        });
        const module = {
            name: Generator.toClassName('RepositoriesModule'),
            location: Generator.toBackendModuleLocation(`@repositories/repositories.module`),
        };
        const repositoriesModule = {
            name: Generator.toBackendModuleName('repositories'),
            moduleClass: module,
            apiModuleRegistration: {
                code: Generator.ts('RepositoriesModule.forRoot(config.repositories)'),
            },
            envConfig: {
                transformer: Generator.ts('repositories: { useInMemory: !val.STATEFUL }'),
                //Stateful is already defined as an app wide setting by the backend core generator
                dotEnvExample: ``,
                decoder: Generator.ts(``),
            },
        };
        const repositories = {
            module,
            interface: {
                name: Generator.toTypeName('Repository'),
                location: Generator.toBackendModuleLocation('@repositories/repository.interface'),
            },
            bulkMutation: {
                name: Generator.toTypeName('BulkMutation'),
                location: Generator.toBackendModuleLocation('@repositories/bulk-mutation.types'),
            },
            bulkMutationForModel: {
                name: Generator.toTypeName('BulkMutationForModel'),
                location: Generator.toBackendModuleLocation('@repositories/bulk-mutation.types'),
            },
            dataService: {
                name: Generator.toClassName('DataService'),
                location: Generator.toBackendModuleLocation('@repositories/data.service'),
                executeBulkMutations: Generator.toFunctionName('executeBulkMutations'),
                executeBulkMutation: Generator.toFunctionName('executeBulkMutation'),
                getAllAsDataModel: Generator.toFunctionName('getAllAsDataModel'),
            },
        };
        context.backend.modules.push(repositoriesModule);
        const models = new Map();
        for (const [modelName, model] of context.models) {
            models.set(modelName, registerModelRepository(model, context));
        }
        const result = {
            ...context,
            repositories,
            models,
        };
        validateModels({ models, context: result });
        return result;
    },
    generate: async (context) => {
        const vfsSrc = new Generator.VirtualFileSystem();
        for (const model of context.models.values()) {
            if (model.repository.kind === 'NoRepository') {
                continue;
            }
            vfsSrc.write(Generator.toLocalFile(model.repository), generateModelRepository(model, context));
            if (model.repository.kind === 'DatabaseCached' || model.repository.kind === 'DatabaseDirect') {
                const inMemoryVersion = extractInMemoryVersionFromDatabaseVersion(model); // NOSONAR
                vfsSrc.write(Generator.toLocalFile(inMemoryVersion.repository), generateModelRepository(inMemoryVersion, context));
            }
        }
        vfsSrc.write(Generator.toLocalFile(context.repositories.module), (0, repository_module_generator_1.generateRepositoriesModule)(context));
        vfsSrc.write(Generator.toLocalFile(context.repositories.interface), (0, repository_interface_generator_1.generateRepositoryInterface)(context));
        vfsSrc.write(Generator.toLocalFile(context.repositories.bulkMutation), (0, bulk_mutation_types_generator_1.generateBulkMutationTypes)(context));
        vfsSrc.write(Generator.toLocalFile(context.repositories.dataService), (0, data_service_generator_1.generateDataService)(context));
        const vfsRepositories = new Generator.VirtualFileSystem();
        vfsRepositories.insertFromVfs({ targetPath: 'src', vfs: vfsSrc });
        context.vfs.insertFromVfs({ targetPath: 'backend/libs/repositories', vfs: vfsRepositories });
        return Promise.resolve(context); // NOSONAR
    },
};
function registerModelRepository(model, context) {
    if (!model.source) {
        throw new Error(`Model ${model.name} does not have a source`);
    }
    try {
        const { repository: { type: kind }, } = model_types_1.zRepository.parse(model.source);
        if (kind === 'NoRepository') {
            return {
                ...model,
                repository: { kind },
            };
        }
        const shared = {
            name: Generator.toClassName(`${model._conjugated.PascalCase}Repository`),
            location: Generator.toBackendModuleLocation(`@repositories/repositories/${model._conjugated.camelCase}.repository`),
            dataServiceVariable: Generator.toVariableName(`${model._conjugated.camelCasePlural}`),
            bulkMutationEntry: Generator.toVariableName(`${model._conjugated.camelCasePlural}`),
        };
        if (kind === 'InMemory') {
            return {
                ...model,
                repository: {
                    kind,
                    ...shared,
                    location: Generator.toBackendModuleLocation(`@repositories/repositories/${model._conjugated.camelCase}.in-memory.repository`),
                    name: Generator.toClassName(`${model._conjugated.PascalCase}Repository`),
                },
            };
        }
        const stateless = {
            ...shared,
            kind: 'InMemory',
            location: Generator.toBackendModuleLocation(`@repositories/repositories/${model._conjugated.camelCase}.in-memory.repository`),
            name: Generator.toClassName(`${model._conjugated.PascalCase}InMemoryRepository`),
        };
        return {
            ...model,
            repository: {
                kind,
                prismaClientName: Generator.toFunctionName(`${model._conjugated.camelCase}`),
                ...shared,
                inMemoryVersion: stateless,
                location: Generator.toBackendModuleLocation(`@repositories/repositories/${model._conjugated.camelCase}.database.repository`),
            },
        };
    }
    catch (e) {
        context.options.errors.push(`Error registering repository for model ${model.name}.\nRepository settings: ${JSON.stringify(model.source.repository)}\nError: ${e}`);
        if (context.options.ignoreErrors) {
            return {
                ...model,
                repository: { kind: 'NoRepository' },
            };
        }
        console.error(`Error registering repository for model ${model.name}: ${e}`);
        throw e;
    }
}
function validateModels({ models, context, }) {
    for (const model of models.values()) {
        if (model.repository.kind === 'NoRepository') {
            continue;
        }
        for (const field of model.fields.values()) {
            validateField({ field, model, models, context });
        }
    }
}
function validateField({ field, model, models, context, }) {
    if (field.kind !== 'relation') {
        return;
    }
    const relatedModel = models.get(field.referencedModelName);
    if (relatedModel.repository.kind === 'NoRepository') {
        const message = `Model ${(0, utils_1.cyan)(model.name)} has a relation to model ${(0, utils_1.cyan)(relatedModel.name)}, which does not have a repository.`;
        context.options.errors.push(message);
        if (!context.options.ignoreErrors) {
            throw new Error(message);
        }
    }
    if (relatedModel.repository.kind === 'InMemory' && model.repository.kind !== 'InMemory') {
        const message = `Model ${(0, utils_1.cyan)(model.name)} (type: ${(0, utils_1.red)(model.repository.kind)}) has a relation to model ${(0, utils_1.cyan)(relatedModel.name)} (type: ${(0, utils_1.red)(relatedModel.repository.kind)})!
        
The types of the repositories must match: an in memory repository cannot reference a database repository and vice versa!`;
        context.options.errors.push(message);
        if (!context.options.ignoreErrors) {
            throw new Error(message);
        }
    }
}
function generateModelRepository(model, context) {
    switch (model.repository.kind) {
        case 'DatabaseDirect':
            return (0, model_repository_generator_1.generateRepository_DatabaseDirect)(model, context); // NOSONAR
        case 'DatabaseCached':
            return (0, model_repository_generator_1.generateRepository_DatabaseCached)(model, context); // NOSONAR
        case 'InMemory':
            return (0, model_repository_generator_1.generateRepository_InMemory)(model, context); // NOSONAR
        default:
            throw new utils_1.ExhaustiveSwitchCheck(model.repository);
    }
}
function extractInMemoryVersionFromDatabaseVersion(model) {
    return {
        ...model,
        repository: {
            ...model.repository,
            kind: 'InMemory',
            name: model.repository.inMemoryVersion.name,
            location: model.repository.inMemoryVersion.location,
        },
    };
}
//# sourceMappingURL=repositories.generator.js.map