"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ManyToManyRelationFieldCrudManager = void 0;
const string_helper_1 = require("../string.helper");
const common_1 = require("@nestjs/common");
const class_validator_1 = require("class-validator");
const create_field_metadata_dto_1 = require("../../dtos/create-field-metadata.dto");
const typeorm_1 = require("typeorm");
const linkCommands = [create_field_metadata_dto_1.RelationFieldsCommand.link, create_field_metadata_dto_1.RelationFieldsCommand.unlink, create_field_metadata_dto_1.RelationFieldsCommand.set];
class ManyToManyRelationFieldCrudManager {
    constructor(options) {
        this.options = options;
        this.logger = new common_1.Logger(ManyToManyRelationFieldCrudManager.name);
        if (!this.options.isInverseSide) {
            this.valueFieldName = `${this.options.fieldName}`;
            this.idFieldName = `${this.options.fieldName}Ids`;
            this.commandFieldName = `${this.options.fieldName}Command`;
        }
        else {
            this.valueFieldName = `${this.options.relationCoModelFieldName}`;
            this.idFieldName = `${this.options.relationCoModelFieldName}Ids`;
            this.commandFieldName = `${this.options.relationCoModelFieldName}Command`;
        }
    }
    validate(dto) {
        return this.applyValidations(dto);
    }
    applyValidations(dto) {
        const errors = [];
        const commandFieldName = this.commandFieldName;
        const commandValue = dto[commandFieldName];
        this.isApplyRequiredValidation() && (0, class_validator_1.isEmpty)(commandValue) ? errors.push({ field: commandFieldName, error: 'Command field is required' }) : "no errors";
        if ((0, class_validator_1.isNotEmpty)(commandValue)) {
            !(0, class_validator_1.isEnum)(commandValue, create_field_metadata_dto_1.RelationFieldsCommand) ? errors.push({ field: this.options.fieldName, error: 'Command Field has invalid value' }) : "no errors";
        }
        var fieldValue = null;
        if (commandValue === create_field_metadata_dto_1.RelationFieldsCommand.clear) {
            return errors;
        }
        else if (linkCommands.includes(commandValue)) {
            fieldValue = dto[this.idFieldName];
        }
        else {
            fieldValue = dto[this.valueFieldName];
        }
        this.isApplyRequiredValidation() && (0, class_validator_1.isEmpty)(fieldValue) ? errors.push({ field: this.options.fieldName, error: `Field: ${this.options.fieldName} is required` }) : "no errors";
        return errors;
    }
    async transformForCreate(dto) {
        const currentEntityName = (0, string_helper_1.classify)(this.options.modelSingularName);
        const currentEntityRepository = this.options.entityManager.getRepository(currentEntityName);
        const relatedEntityName = (0, string_helper_1.classify)(this.options.relationCoModelSingularName);
        const relatedEntityRepository = this.options.entityManager.getRepository(relatedEntityName);
        const result = await this.transformByCommand(dto, relatedEntityRepository, currentEntityRepository);
        if (result !== undefined) {
            dto[this.valueFieldName] = result;
        }
        else {
            delete dto[this.valueFieldName];
        }
        return dto;
    }
    async transformByCommand(dto, relatedEntityRepository, currentEntityRepository) {
        const command = dto[this.commandFieldName];
        const values = dto[this.valueFieldName];
        const ids = dto[this.idFieldName];
        switch (command) {
            case create_field_metadata_dto_1.RelationFieldsCommand.create:
                return await this.transformForCommandCreate(values, relatedEntityRepository);
            case create_field_metadata_dto_1.RelationFieldsCommand.update:
                return await this.transformForCommandUpdate(values, relatedEntityRepository, dto, currentEntityRepository);
            case create_field_metadata_dto_1.RelationFieldsCommand.delete:
                return await this.transformForCommandDelete(values, relatedEntityRepository);
            case create_field_metadata_dto_1.RelationFieldsCommand.clear:
                return this.transformForCommandClear();
            case create_field_metadata_dto_1.RelationFieldsCommand.set:
                return await this.transformForCommandSet(ids, relatedEntityRepository);
            case create_field_metadata_dto_1.RelationFieldsCommand.link:
                return await this.tranformForCommandLink(ids, relatedEntityRepository);
            case create_field_metadata_dto_1.RelationFieldsCommand.unlink:
                return await this.transformForCommandUnLink(ids);
            default:
                this.logger.log(`Invalid command ${command}`);
                return null;
        }
    }
    transformForCommandClear() {
        return [];
    }
    async transformForCommandSet(ids, relatedEntityRepository) {
        const loadedEntities = await relatedEntityRepository.find({
            where: { id: (0, typeorm_1.In)(ids) }
        });
        if (loadedEntities.length !== ids.length) {
            throw new Error('Invalid entity ids provided for set');
        }
        return loadedEntities;
    }
    async tranformForCommandLink(ids, relatedEntityRepository) {
        if (!this.options.isUpdate) {
            throw new Error('Link command is only supported for update operations');
        }
        const count = await relatedEntityRepository.count({
            where: { id: (0, typeorm_1.In)(ids) }
        });
        if (count !== ids.length) {
            throw new Error('Invalid entity ids provided for linking');
        }
        await this.options.entityManager
            .createQueryBuilder()
            .relation((0, string_helper_1.classify)(this.options.modelSingularName), this.valueFieldName)
            .of(this.options.entityId)
            .add(ids);
        return undefined;
    }
    async transformForCommandUnLink(ids) {
        if (!this.options.isUpdate) {
            throw new Error('Unlink command is only supported for update operations');
        }
        await this.options.entityManager
            .createQueryBuilder()
            .relation((0, string_helper_1.classify)(this.options.modelSingularName), this.valueFieldName)
            .of(this.options.entityId)
            .remove(ids);
        return undefined;
    }
    async transformForCommandCreate(values, relatedEntityRepository) {
        const transformedRelatedFields = [];
        for (const entity of values) {
            const transformed = relatedEntityRepository.create(entity);
            transformedRelatedFields.push(transformed);
        }
        return transformedRelatedFields;
    }
    async transformForCommandUpdate(values, relatedEntityRepository, dto, currentEntityRepository) {
        const transformedRelatedFields = [];
        for (const entity of values) {
            if (entity.id) {
                const transformed = await relatedEntityRepository.preload(entity);
                transformedRelatedFields.push(transformed);
            }
            else {
                const transformed = relatedEntityRepository.create(entity);
                transformedRelatedFields.push(transformed);
            }
        }
        return transformedRelatedFields;
    }
    async transformForCommandDelete(values, relatedEntityRepository) {
        const ids = values.map((value) => value.id);
        await relatedEntityRepository.delete(ids);
        return null;
    }
    isApplyRequiredValidation() {
        return this.options.required;
    }
}
exports.ManyToManyRelationFieldCrudManager = ManyToManyRelationFieldCrudManager;
//# sourceMappingURL=ManyToManyRelationFieldCrudManager.js.map