import JDLObject from '../models/jdl-object.js';
import { JDLEntity, JDLEnum } from '../models/index.mjs';
import JDLField from '../models/jdl-field.js';
import JDLValidation from '../models/jdl-validation.js';
import JDLRelationship from '../models/jdl-relationship.js';
import JDLUnaryOption from '../models/jdl-unary-option.js';
import JDLBinaryOption from '../models/jdl-binary-option.js';
import { lowerFirst, upperFirst } from '../utils/string-utils.js';
import { fieldTypes, unaryOptions, binaryOptions, relationshipOptions } from '../jhipster/index.mjs';
import { asJdlRelationshipType } from '../jhipster/relationship-types.js';
const { BlobTypes, CommonDBTypes, RelationalOnlyDBTypes } = fieldTypes;
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;
const { ANY, IMAGE, TEXT } = BlobTypes;
const { BYTES } = RelationalOnlyDBTypes;
export default {
    convertEntitiesToJDL,
};
let entities;
let jdlObject;
export function convertEntitiesToJDL(params) {
    if (!params.entities) {
        throw new Error('Entities have to be passed to be converted.');
    }
    init(params);
    addEntities();
    addRelationshipsToJDL();
    return jdlObject;
}
function init(params) {
    entities = params.entities;
    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,
    });
    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,
    });
    if (jdlField.type === BYTES) {
        jdlField.type = getTypeForBlob(field.fieldTypeBlobContent);
    }
    if (field.fieldValidateRules) {
        addValidations(jdlField, field);
    }
    return jdlField;
}
function getTypeForBlob(blobContentType) {
    if ([ANY, IMAGE, TEXT].includes(blobContentType)) {
        return CommonDBTypes[`${blobContentType.toUpperCase()}_BLOB`];
    }
    throw new Error(`Unrecognised blob type: '${blobContentType}'`);
}
function addValidations(jdlField, field) {
    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.includes('(')) {
            const [key, value] = fieldValue
                .replace(/^(\w+)\s\((\w+)\)$/, (match, matchedKey, matchedValue) => `${matchedKey},${matchedValue}`)
                .split(',');
            return {
                key,
                value,
            };
        }
        return { key: fieldValue };
    });
}
function addRelationshipsToJDL() {
    entities.forEach((entity, entityName) => {
        dealWithRelationships(entity.relationships, entityName);
    });
}
function dealWithRelationships(relationships, entityName) {
    if (!relationships) {
        return;
    }
    relationships.forEach(relationship => {
        if (relationship.relationshipSide === 'right') {
            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,
    };
    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 {};
    }
    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,
    };
}
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);
    }
    if (entity.angularJSSuffix) {
        addBinaryOptionToJDL(ANGULAR_SUFFIX, entity.angularJSSuffix, entityName);
    }
    if (entity.microserviceName !== undefined) {
        addBinaryOptionToJDL(MICROSERVICE, entity.microserviceName, entityName);
    }
    if (entity.jpaMetamodelFiltering === true) {
        addUnaryOptionToJDL(FILTER, entityName);
    }
    if (entity.readOnly === true) {
        addUnaryOptionToJDL(READ_ONLY, entityName);
    }
    if (entity.embedded === true) {
        addUnaryOptionToJDL(EMBEDDED, entityName);
    }
    if (entity.clientRootFolder) {
        addBinaryOptionToJDL(CLIENT_ROOT_FOLDER, entity.clientRootFolder, entityName);
    }
}
function addUnaryOptionToJDL(unaryOption, entityName) {
    jdlObject.addOption(new JDLUnaryOption({
        name: unaryOption,
        entityNames: [entityName],
    }));
}
function addBinaryOptionToJDL(binaryOption, value, entityName) {
    jdlObject.addOption(new JDLBinaryOption({
        name: binaryOption,
        value,
        entityNames: [entityName],
    }));
}
