/**
 * Copyright 2013-2026 the original author or authors from the JHipster project.
 *
 * This file is part of the JHipster project, see https://www.jhipster.tech/
 * for more information.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { lowerFirst, upperFirst } from 'lodash-es';
import { asJdlRelationshipType } from "../core/basic-types/relationship-types.js";
import { binaryOptions, relationshipOptions, unaryOptions } from "../core/built-in-options/index.js";
import { JDLEntity, JDLEnum } from "../core/models/index.js";
import JDLBinaryOption from "../core/models/jdl-binary-option.js";
import JDLField from "../core/models/jdl-field.js";
import JDLObject from "../core/models/jdl-object.js";
import JDLRelationship, {} from "../core/models/jdl-relationship.js";
import JDLUnaryOption from "../core/models/jdl-unary-option.js";
import JDLValidation from "../core/models/jdl-validation.js";
const { BUILT_IN_ENTITY } = relationshipOptions;
const { FILTER, NO_FLUENT_METHOD, READ_ONLY, EMBEDDED } = unaryOptions;
const { ANGULAR_SUFFIX, CLIENT_ROOT_FOLDER, DTO, MICROSERVICE, PAGINATION, SEARCH, SERVICE } = binaryOptions.Options;
export default {
    convertEntitiesToJDL,
};
let entities;
let jdlObject;
/**
 * Convert the passed entities (parsed from JSON files) to a JDL object.
 * @param entities - a Map having for keys the entity names and values the JSON entity files.
 * @return the parsed entities in the JDL form.
 */
export function convertEntitiesToJDL(entities) {
    if (!entities) {
        throw new Error('Entities have to be passed to be converted.');
    }
    init(entities);
    addEntities();
    addRelationshipsToJDL();
    return jdlObject;
}
function init(ents) {
    entities = ents;
    jdlObject = new JDLObject();
}
function addEntities() {
    entities.forEach((entity, entityName) => {
        addEntity(entity, entityName);
    });
}
function addEntity(entity, entityName) {
    jdlObject.addEntity(convertJSONToJDLEntity(entity, entityName));
    addEnumsToJDL(entity);
    addEntityOptionsToJDL(entity, entityName);
}
function convertJSONToJDLEntity(entity, entityName) {
    const jdlEntity = new JDLEntity({
        name: entityName,
        tableName: entity.entityTableName,
        comment: entity.documentation,
        annotations: entity.annotations,
    });
    addFields(jdlEntity, entity);
    return jdlEntity;
}
function addFields(jdlEntity, entity) {
    entity?.fields?.forEach(field => {
        jdlEntity.addField(convertJSONToJDLField(field));
    });
}
function convertJSONToJDLField(field) {
    const jdlField = new JDLField({
        name: lowerFirst(field.fieldName),
        type: field.fieldType,
        comment: field.documentation,
        options: field.options,
    });
    addValidations(jdlField, field);
    return jdlField;
}
function addValidations(jdlField, field) {
    if (field.fieldValidateRules) {
        field.fieldValidateRules.forEach((rule) => {
            jdlField.addValidation(convertJSONToJDLValidation(rule, field));
        });
    }
}
function convertJSONToJDLValidation(rule, field) {
    return new JDLValidation({
        name: rule,
        value: field[`fieldValidateRules${upperFirst(rule)}`],
    });
}
function addEnumsToJDL(entity) {
    entity?.fields?.forEach((field) => {
        if (field.fieldValues !== undefined) {
            jdlObject.addEnum(new JDLEnum({
                name: field.fieldType,
                values: getEnumValuesFromString(field.fieldValues),
                comment: field.fieldTypeDocumentation,
            }));
        }
    });
}
function getEnumValuesFromString(valuesAsString) {
    return valuesAsString.split(',').map(fieldValue => {
        // if fieldValue looks like ENUM_VALUE (something)
        if (fieldValue.includes('(')) {
            const [key, value] = fieldValue
                .replace(/^(\w+)\s\((\w+)\)$/, (_match, matchedKey, matchedValue) => `${matchedKey},${matchedValue}`)
                .split(',');
            return {
                key,
                value,
            };
        }
        return { key: fieldValue };
    });
}
/*
 * Adds relationships for entities to JDL.
 * The jdl passed must contain the jdl entities concerned by the relationships
 */
function addRelationshipsToJDL() {
    entities.forEach((entity, entityName) => {
        dealWithRelationships(entity.relationships, entityName);
    });
}
function dealWithRelationships(relationships, entityName) {
    if (!relationships) {
        return;
    }
    relationships.forEach(relationship => {
        if (relationship.relationshipSide === 'right') {
            // Right side will be merged into the left side.
            return;
        }
        const jdlRelationship = getRelationship(relationship, entityName);
        if (jdlRelationship) {
            jdlObject.addRelationship(jdlRelationship);
        }
    });
}
function getRelationship(relationship, entityName) {
    const type = asJdlRelationshipType(relationship.relationshipType);
    const options = getRelationshipOptions(relationship);
    const sourceEntitySideAttributes = getSourceEntitySideAttributes(entityName, relationship);
    const destinationEntityName = upperFirst(relationship.otherEntityName);
    const destinationJDLEntity = jdlObject.getEntity(destinationEntityName);
    const destinationEntity = entities.get(destinationEntityName);
    let relationshipConfiguration = {
        side: relationship.relationshipSide,
        from: entityName,
        to: destinationJDLEntity?.name ?? destinationEntityName,
        type,
        options,
        injectedFieldInFrom: sourceEntitySideAttributes.injectedFieldInSourceEntity,
        isInjectedFieldInFromRequired: sourceEntitySideAttributes.injectedFieldInSourceIsRequired,
        commentInFrom: sourceEntitySideAttributes.commentForSourceEntity,
        isInjectedFieldInToRequired: false,
        injectedFieldInTo: null,
        commentInTo: null,
    };
    relationshipConfiguration = {
        ...relationshipConfiguration,
        ...sourceEntitySideAttributes,
    };
    if (!destinationJDLEntity || !destinationEntity) {
        if (relationshipConfiguration.options.global[BUILT_IN_ENTITY]) {
            return new JDLRelationship(relationshipConfiguration);
        }
        return undefined;
    }
    const isEntityTheDestinationSideEntity = (otherEntityName, otherEntityRelationshipName) => otherEntityName === entityName && otherEntityRelationshipName === relationship.relationshipName;
    const destinationSideAttributes = getDestinationEntitySideAttributes(isEntityTheDestinationSideEntity, destinationEntity.relationships);
    relationshipConfiguration = {
        ...relationshipConfiguration,
        injectedFieldInTo: destinationSideAttributes.injectedFieldInDestinationEntity,
        isInjectedFieldInToRequired: destinationSideAttributes.injectedFieldInDestinationIsRequired ?? false,
        commentInTo: destinationSideAttributes.commentForDestinationEntity,
    };
    if (destinationSideAttributes.optionsForDestinationEntity) {
        relationshipConfiguration.options = {
            ...relationshipConfiguration.options,
            destination: destinationSideAttributes.optionsForDestinationEntity,
        };
    }
    return new JDLRelationship(relationshipConfiguration);
}
function getSourceEntitySideAttributes(entityName, relationship) {
    return {
        sourceEntity: entityName,
        injectedFieldInSourceEntity: getInjectedFieldInSourceEntity(relationship),
        injectedFieldInSourceIsRequired: relationship.relationshipValidateRules,
        commentForSourceEntity: relationship.documentation,
    };
}
function getDestinationEntitySideAttributes(isEntityTheDestinationSideEntity, destinationEntityRelationships) {
    const foundDestinationSideEntity = destinationEntityRelationships?.find(destinationEntityFromRelationship => {
        return isEntityTheDestinationSideEntity(upperFirst(destinationEntityFromRelationship.otherEntityName), destinationEntityFromRelationship.otherEntityRelationshipName);
    });
    if (!foundDestinationSideEntity) {
        return {};
    }
    // Bidirectional relationship
    let injectedFieldInDestinationEntity = foundDestinationSideEntity.relationshipName;
    if (!!foundDestinationSideEntity.otherEntityField && foundDestinationSideEntity.otherEntityField !== 'id') {
        injectedFieldInDestinationEntity += `(${foundDestinationSideEntity.otherEntityField})`;
    }
    const injectedFieldInDestinationIsRequired = !!foundDestinationSideEntity.relationshipValidateRules;
    const commentForDestinationEntity = foundDestinationSideEntity.documentation;
    return {
        injectedFieldInDestinationEntity,
        injectedFieldInDestinationIsRequired,
        commentForDestinationEntity,
        optionsForDestinationEntity: foundDestinationSideEntity.options,
    };
}
function getRelationshipOptions(relationship) {
    const options = {
        global: {},
        source: relationship.options ?? {},
        destination: {},
    };
    if (relationship.relationshipWithBuiltInEntity) {
        options.global[BUILT_IN_ENTITY] = true;
    }
    return options;
}
function getInjectedFieldInSourceEntity(relationship) {
    return (relationship.relationshipName +
        (relationship.otherEntityField && relationship.otherEntityField !== 'id' ? `(${relationship.otherEntityField})` : ''));
}
function addEntityOptionsToJDL(entity, entityName) {
    if (entity.fluentMethods === false) {
        addUnaryOptionToJDL(NO_FLUENT_METHOD, entityName);
    }
    [DTO, PAGINATION, SERVICE].forEach(option => {
        if (entity[option] && entity[option] !== 'no') {
            addBinaryOptionToJDL(option, entity[option], entityName);
        }
    });
    if (entity.searchEngine) {
        addBinaryOptionToJDL(SEARCH, entity.searchEngine, entityName);
    }
    // angularSuffix in BinaryOptions, angularJSSuffix in Json
    if (entity.angularJSSuffix) {
        addBinaryOptionToJDL(ANGULAR_SUFFIX, entity.angularJSSuffix, entityName);
    }
    // microservice in BinaryOptions, microserviceName in Json
    if (entity.microserviceName !== undefined) {
        addBinaryOptionToJDL(MICROSERVICE, entity.microserviceName, entityName);
    }
    if (entity.jpaMetamodelFiltering) {
        addUnaryOptionToJDL(FILTER, entityName);
    }
    if (entity.readOnly) {
        addUnaryOptionToJDL(READ_ONLY, entityName);
    }
    if (entity.embedded) {
        addUnaryOptionToJDL(EMBEDDED, entityName);
    }
    if (entity.clientRootFolder) {
        addBinaryOptionToJDL(CLIENT_ROOT_FOLDER, entity.clientRootFolder, entityName);
    }
}
function addUnaryOptionToJDL(unaryOption, entityName) {
    jdlObject.addOption(new JDLUnaryOption({
        name: unaryOption,
        entityNames: new Set([entityName]),
    }));
}
function addBinaryOptionToJDL(binaryOption, value, entityName) {
    jdlObject.addOption(new JDLBinaryOption({
        name: binaryOption,
        value,
        entityNames: new Set([entityName]),
    }));
}
