"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ContentTypeBuilderService = void 0;
const collation_type_1 = require("./collation-type");
const create_content_type_builder_input_1 = require("./dto/create-content-type-builder.input");
const update_content_type_builder_input_1 = require("./dto/update-content-type-builder.input");
const generate_content_type_module_1 = require("./generate-content-type-module");
const generate_content_type_schema_1 = require("./generate-content-type-schema");
const common_1 = require("@moonlightjs/common");
const common_2 = require("@nestjs/common");
const change_case_1 = require("change-case");
const child_process_1 = require("child_process");
const fs = require("fs-extra");
const promises_1 = require("fs/promises");
const path = require("path");
const util = require("util");
const { MigrateDev, MigrateDeploy, } = require('@modules/content-type-builder/packages/prisma');
const getDirectories = async (source) => (await (0, promises_1.readdir)(source, { withFileTypes: true }))
    .filter((dirent) => dirent.isDirectory())
    .map((dirent) => dirent.name);
const execPromise = util.promisify(child_process_1.exec);
const spawnPromise = (command, args, options) => {
    return new Promise((resolve, reject) => {
        const shell = (0, child_process_1.spawn)(command, args, options);
        shell.on('error', (error) => {
            console.log(`error: ${error.message}`);
            reject(error);
        });
        shell.on('close', (code) => {
            console.log(`[shell] ${command} terminated :`, code);
            resolve(null);
        });
    });
};
let ContentTypeBuilderService = class ContentTypeBuilderService {
    constructor(prisma) {
        this.prisma = prisma;
    }
    async shutdown() {
        await this.cleanup();
        module.hot.dispose(() => { });
        module.hot.accept(() => window.location.reload());
    }
    async cleanup() {
    }
    async get() {
        const data = await this.prisma.contentType.findFirst();
        if (!(data && data.contentTypesSchema)) {
            throw new common_2.NotFoundException();
        }
        return data.contentTypesSchema;
    }
    async onModuleInit() {
        if ((0, common_1.isDevelopment)()) {
            const migrateDev = MigrateDev.new();
            await migrateDev.parse(['--force']);
        }
        else {
            const migrateDeploy = MigrateDeploy.new();
            await migrateDeploy.parse([]);
        }
        const rootFolder = process.env.INIT_CWD;
        let listPathFolderContentTypes = [];
        try {
            listPathFolderContentTypes = await getDirectories(`${rootFolder}/src/content-type`);
        }
        catch (error) {
            listPathFolderContentTypes = [];
        }
        const data = await this.prisma.contentType.findFirst();
        let contentTypesSchema;
        if (data && data.contentTypesSchema) {
            contentTypesSchema = data.contentTypesSchema;
        }
        else {
            contentTypesSchema = {};
        }
        await Promise.all(listPathFolderContentTypes.map(async (contentTypePath) => {
            const schema = await (0, promises_1.readFile)(`${rootFolder}/src/content-type/${contentTypePath}/content-types/schema.json`, 'utf8');
            const contentType = JSON.parse(schema);
            contentTypesSchema[contentType.uid] = contentType;
        }));
        if (data) {
            await this.prisma.contentType.update({
                where: {
                    id: data.id,
                },
                data: {
                    contentTypesSchema: contentTypesSchema,
                },
            });
        }
        else {
            await this.prisma.contentType.create({
                data: {
                    contentTypesSchema: contentTypesSchema,
                },
            });
        }
    }
    async update(uid, input) {
        const data = await this.prisma.contentType.findFirst();
        if (!(data && data.contentTypesSchema)) {
            throw new common_2.BadRequestException();
        }
        const contentTypesSchema = data.contentTypesSchema;
        if (!(uid in contentTypesSchema)) {
            throw new common_2.NotFoundException('content type not found');
        }
        const contentType = {
            attributes: input.attributes,
            collectionName: input.collectionName,
            info: {
                description: input.description,
                displayName: input.displayName,
                pluralName: input.pluralName,
                singularName: input.singularName,
            },
            options: {
                draftAndPublish: input.draftAndPublish,
                softDelete: input.softDelete,
            },
            uid,
        };
        await this.process(uid, contentType, contentTypesSchema, data, true);
        this.shutdown();
    }
    async create(input) {
        let data = await this.prisma.contentType.findFirst();
        let contentTypesSchema;
        if (data && data.contentTypesSchema) {
            contentTypesSchema = data.contentTypesSchema;
        }
        else {
            contentTypesSchema = {};
        }
        const uid = `api::${(0, change_case_1.paramCase)(input.collectionName)}`;
        if (uid in contentTypesSchema) {
            throw new common_2.BadRequestException('Content type already defined');
        }
        const contentType = {
            attributes: input.attributes,
            collectionName: input.collectionName,
            info: {
                description: input.description,
                displayName: input.displayName,
                pluralName: input.pluralName,
                singularName: input.singularName,
            },
            options: {
                draftAndPublish: input.draftAndPublish,
                softDelete: input.softDelete,
            },
            uid,
        };
        data = await this.process(uid, contentType, contentTypesSchema, data);
        this.shutdown();
        return data;
    }
    async delete(uid) {
        const data = await this.prisma.contentType.findFirst();
        if (!(data && data.contentTypesSchema)) {
            throw new common_2.BadRequestException();
        }
        const contentTypesSchema = data.contentTypesSchema;
        if (uid in contentTypesSchema) {
            await this.handleDelete(uid, contentTypesSchema, data);
        }
        else {
            throw new common_2.NotFoundException(`Not found content type ${uid}`);
        }
        this.shutdown();
    }
    async process(uid, contentType, contentTypesSchema, data, isUpdate = false) {
        let result = data;
        Object.keys(contentType.attributes).forEach((attributeName) => {
            const attribute = contentType.attributes[attributeName];
            if (attribute.type === 'relation') {
                const attrRelation = attribute;
                attrRelation.inversedBy = attributeName;
                const target = contentTypesSchema[attrRelation.target];
                if (!target)
                    throw new common_2.BadRequestException(`${attrRelation.target} does not exist`);
                switch (attrRelation.relation) {
                    case 'oneToOne':
                        const oneToOneRelation = {
                            configurable: attrRelation.configurable,
                            private: attrRelation.private,
                            relation: 'oneToOne',
                            required: attrRelation.required,
                            target: uid,
                            targetAttribute: attributeName,
                            type: 'relation',
                            unique: attrRelation.unique,
                            visible: attrRelation.visible,
                            writable: attrRelation.writable,
                            mappedBy: attributeName,
                        };
                        target.attributes[attrRelation.targetAttribute] = oneToOneRelation;
                        break;
                    case 'manyToOne':
                        const oneToManyRelation = {
                            configurable: attrRelation.configurable,
                            private: attrRelation.private,
                            relation: 'oneToMany',
                            required: attrRelation.required,
                            target: uid,
                            targetAttribute: attributeName,
                            type: 'relation',
                            unique: attrRelation.unique,
                            visible: attrRelation.visible,
                            writable: attrRelation.writable,
                            mappedBy: attributeName,
                        };
                        target.attributes[attrRelation.targetAttribute] = oneToManyRelation;
                        break;
                    case 'oneToMany':
                        const manyToOneRelation = {
                            configurable: attrRelation.configurable,
                            private: attrRelation.private,
                            relation: 'manyToOne',
                            required: attrRelation.required,
                            target: uid,
                            targetAttribute: attributeName,
                            type: 'relation',
                            unique: attrRelation.unique,
                            visible: attrRelation.visible,
                            writable: attrRelation.writable,
                            mappedBy: attributeName,
                        };
                        target.attributes[attrRelation.targetAttribute] = manyToOneRelation;
                        break;
                    case 'manyToMany':
                        const manyToManyRelation = {
                            configurable: attrRelation.configurable,
                            private: attrRelation.private,
                            relation: 'manyToMany',
                            required: attrRelation.required,
                            target: uid,
                            targetAttribute: attributeName,
                            type: 'relation',
                            unique: attrRelation.unique,
                            visible: attrRelation.visible,
                            writable: attrRelation.writable,
                            mappedBy: attributeName,
                        };
                        target.attributes[attrRelation.targetAttribute] =
                            manyToManyRelation;
                        break;
                }
            }
        });
        contentTypesSchema[uid] = contentType;
        if (result) {
            result.contentTypesSchema = contentTypesSchema;
            await this.prisma.contentType.update({
                where: {
                    id: result.id,
                },
                data: {
                    contentTypesSchema: contentTypesSchema,
                },
            });
        }
        else {
            result = await this.prisma.contentType.create({
                data: {
                    contentTypesSchema: contentTypesSchema,
                },
            });
        }
        (0, generate_content_type_schema_1.generateContentTypeSchema)(contentTypesSchema);
        (0, generate_content_type_module_1.generateContentTypesModule)(contentTypesSchema);
        const rootFolder = process.env.INIT_CWD;
        const execAurora = await execPromise(`cd ${rootFolder} && npm run aurora`);
        console.log('stdout:', execAurora.stdout);
        console.log('stderr:', execAurora.stderr);
        const migrateDev = MigrateDev.new();
        await migrateDev.parse([
            '-n',
            `${isUpdate ? 'update' : 'create'}-${(0, change_case_1.paramCase)(contentType.collectionName)}-model`,
            '--force',
        ]);
        const execFormat = await execPromise(`cd ${rootFolder} && npm run format`);
        console.log('stdout:', execFormat.stdout);
        console.log('stderr:', execFormat.stderr);
        return result;
    }
    async handleDelete(uid, contentTypesSchema, data) {
        let result = data;
        const contentType = contentTypesSchema[uid];
        Object.keys(contentType.attributes).forEach((attributeName) => {
            const attribute = contentType.attributes[attributeName];
            if (attribute.type === 'relation') {
                const attrRelation = attribute;
                attrRelation.inversedBy = attributeName;
                const target = contentTypesSchema[attrRelation.target];
                if (!target)
                    throw new common_2.BadRequestException(`${attrRelation.target} does not exist`);
                delete target.attributes[attrRelation.targetAttribute];
            }
        });
        delete contentTypesSchema[uid];
        if (result) {
            result.contentTypesSchema = contentTypesSchema;
            await this.prisma.contentType.update({
                where: {
                    id: result.id,
                },
                data: {
                    contentTypesSchema: contentTypesSchema,
                },
            });
        }
        else {
            result = await this.prisma.contentType.create({
                data: {
                    contentTypesSchema: contentTypesSchema,
                },
            });
        }
        const rootFolder = process.env.INIT_CWD;
        try {
            fs.removeSync(`${rootFolder}/src/content-type/${(0, change_case_1.paramCase)(contentType.collectionName)}`);
        }
        catch (err) {
            console.warn(err);
        }
        (0, generate_content_type_schema_1.generateContentTypeSchema)(contentTypesSchema);
        (0, generate_content_type_module_1.generateContentTypesModule)(contentTypesSchema);
        const execAurora = await execPromise(`cd ${rootFolder} && npm run aurora`);
        console.log('stdout:', execAurora.stdout);
        console.log('stderr:', execAurora.stderr);
        const migrateDev = MigrateDev.new();
        await migrateDev.parse([
            '-n',
            `delete-${(0, change_case_1.paramCase)(contentType.collectionName)}-model`,
            '--force',
        ]);
        const execFormat = await execPromise(`cd ${rootFolder} && npm run format`);
        console.log('stdout:', execFormat.stdout);
        console.log('stderr:', execFormat.stderr);
        return result;
    }
    removeImportFromAppModule(rootFolder, contentType) {
        const appModulePath = path.join(rootFolder, 'src/app.module.ts');
        let appModuleContent = fs.readFileSync(appModulePath, {
            encoding: 'utf8',
        });
        const importStr = `import { ${(0, change_case_1.pascalCase)(contentType.collectionName)}Module } from '@content-type/${(0, change_case_1.paramCase)(contentType.collectionName)}/${(0, change_case_1.paramCase)(contentType.collectionName)}.module';`;
        const moduleName = `${(0, change_case_1.pascalCase)(contentType.collectionName)}Module`;
        appModuleContent = appModuleContent.replace(importStr, '');
        appModuleContent = appModuleContent.replace(moduleName + ',', '');
        appModuleContent = appModuleContent.replace(moduleName, '');
        fs.writeFileSync(`${appModulePath}`, appModuleContent, {
            encoding: 'utf-8',
        });
    }
};
ContentTypeBuilderService = __decorate([
    (0, common_2.Injectable)(),
    __metadata("design:paramtypes", [common_1.PrismaService])
], ContentTypeBuilderService);
exports.ContentTypeBuilderService = ContentTypeBuilderService;
//# sourceMappingURL=content-type-builder.service.js.map