import { uniqBy } from 'lodash-es';
import { applicationOptions } from '../jhipster/index.js';
import { readCurrentPathYoRcFile } from '../utils/yo-rc.js';
import ParsedJDLToJDLObjectConverter from './converters/parsed-jdl-to-jdl-object/parsed-jdl-to-jdl-object-converter.js';
import JDLWithoutApplicationToJSONConverter from './converters/jdl-to-json/jdl-without-application-to-json-converter.js';
import { convert } from './converters/jdl-to-json/jdl-with-applications-to-json-converter.js';
import { formatApplicationToExport, formatApplicationsToExport, } from './converters/exporters/applications/jhipster-application-formatter.js';
import exportDeployments from './converters/exporters/jhipster-deployment-exporter.js';
import exportEntities from './converters/exporters/jhipster-entity-exporter.js';
import { GENERATOR_NAME } from './converters/exporters/export-utils.js';
import { parseFromContent, parseFromFiles } from './core/readers/jdl-reader.js';
import createWithApplicationValidator from './converters/validators/jdl-with-application-validator.js';
import createWithoutApplicationValidator from './converters/validators/jdl-without-application-validator.js';
import { createRuntime, getDefaultRuntime } from './core/runtime.js';
const { OptionNames } = applicationOptions;
const { APPLICATION_TYPE, BASE_NAME } = OptionNames;
const GENERATOR_BEGCODE = 'generator-begcode';
export function createImporterFromFiles(files, configuration, definition) {
    if (!files) {
        throw new Error('Files must be passed to create a new JDL importer.');
    }
    const runtime = definition ? createRuntime(definition) : getDefaultRuntime();
    const content = parseFromFiles(files, runtime);
    return makeJDLImporter(content, configuration || {}, runtime);
}
export function createImporterFromContent(jdlString, configuration, definition) {
    if (!jdlString) {
        throw new Error('A JDL content must be passed to create a new JDL importer.');
    }
    const runtime = definition ? createRuntime(definition) : getDefaultRuntime();
    const content = parseFromContent(jdlString, runtime);
    return makeJDLImporter(content, configuration || {}, runtime);
}
function makeJDLImporter(content, configuration, runtime) {
    let importState = {
        exportedApplications: [],
        exportedApplicationsWithEntities: {},
        exportedEntities: [],
        exportedDeployments: [],
    };
    return {
        import: (logger = console) => {
            const jdlObject = getJDLObject(content, configuration, runtime);
            checkForErrors(jdlObject, configuration, logger);
            if (jdlObject.getApplicationQuantity() === 0 && jdlObject.getEntityQuantity() > 0) {
                importState.exportedEntities = importOnlyEntities(jdlObject, configuration);
            }
            else if (jdlObject.getApplicationQuantity() === 1) {
                importState = importOneApplicationAndEntities(jdlObject);
            }
            else {
                importState = importApplicationsAndEntities(jdlObject);
            }
            if (jdlObject.getDeploymentQuantity()) {
                importState.exportedDeployments = importDeployments(jdlObject.deployments);
            }
            return importState;
        },
    };
}
function getJDLObject(parsedJDLContent, configuration, runtime) {
    let baseName = configuration.applicationName;
    let applicationType = configuration.applicationType;
    let databaseType = configuration.databaseType;
    if (configuration.application) {
        baseName = configuration.application[GENERATOR_BEGCODE].baseName;
        applicationType = configuration.application[GENERATOR_BEGCODE].applicationType;
        databaseType = configuration.application[GENERATOR_BEGCODE].databaseType;
    }
    return ParsedJDLToJDLObjectConverter.parseFromConfigurationObject({
        parsedContent: parsedJDLContent,
        applicationType,
        applicationName: baseName,
        databaseType,
    }, runtime);
}
function checkForErrors(jdlObject, configuration, logger = console) {
    let validator;
    if (jdlObject.getApplicationQuantity() === 0) {
        let application = configuration.application;
        if (!application) {
            application = readCurrentPathYoRcFile();
        }
        let applicationType = configuration.applicationType;
        let databaseType = configuration.databaseType;
        let blueprints = configuration.blueprints;
        if (application?.[GENERATOR_BEGCODE]) {
            if (applicationType === undefined) {
                applicationType = application[GENERATOR_BEGCODE].applicationType;
            }
            if (databaseType === undefined) {
                databaseType = application[GENERATOR_BEGCODE].databaseType;
            }
            if (blueprints === undefined) {
                blueprints = application[GENERATOR_BEGCODE].blueprints;
            }
        }
        validator = createWithoutApplicationValidator(jdlObject, {
            applicationType,
            databaseType,
            blueprints,
        }, logger);
    }
    else {
        validator = createWithApplicationValidator(jdlObject, logger);
    }
    validator.checkForErrors();
}
function importOnlyEntities(jdlObject, configuration) {
    let { applicationName, applicationType, databaseType } = configuration;
    let application = configuration.application;
    if (!application) {
        application = readCurrentPathYoRcFile();
    }
    if (application?.[GENERATOR_BEGCODE]) {
        if (applicationType === undefined) {
            applicationType = application[GENERATOR_BEGCODE].applicationType;
        }
        if (applicationName === undefined) {
            applicationName = application[GENERATOR_BEGCODE].baseName;
        }
        if (databaseType === undefined) {
            databaseType = application[GENERATOR_BEGCODE].databaseType;
        }
    }
    const entitiesPerApplicationMap = JDLWithoutApplicationToJSONConverter.convert({
        jdlObject,
        applicationName,
        applicationType,
        databaseType,
    });
    const jsonEntities = entitiesPerApplicationMap.get(applicationName);
    return exportJSONEntities(jsonEntities, configuration);
}
function importOneApplicationAndEntities(jdlObject) {
    const importState = {
        exportedApplications: [],
        exportedApplicationsWithEntities: {},
        exportedEntities: [],
        exportedDeployments: [],
    };
    const formattedApplication = formatApplicationToExport(jdlObject.getApplications()[0]);
    importState.exportedApplications.push(formattedApplication);
    const jdlApplication = jdlObject.getApplications()[0];
    const applicationName = jdlApplication.getConfigurationOptionValue(BASE_NAME);
    const entitiesPerApplicationMap = convert({
        jdlObject,
    });
    const jsonEntities = entitiesPerApplicationMap.get(applicationName);
    const { [GENERATOR_NAME]: config, ...remaining } = formattedApplication;
    importState.exportedApplicationsWithEntities[applicationName] = {
        config,
        ...remaining,
        entities: [],
    };
    if (jsonEntities.length !== 0) {
        const exportedJSONEntities = exportJSONEntities(jsonEntities, {
            applicationName,
            applicationType: jdlApplication.getConfigurationOptionValue(APPLICATION_TYPE),
            forSeveralApplications: false,
        });
        importState.exportedApplicationsWithEntities[applicationName].entities = exportedJSONEntities;
        importState.exportedEntities = uniqBy([...importState.exportedEntities, ...exportedJSONEntities], 'name');
    }
    return importState;
}
function importApplicationsAndEntities(jdlObject) {
    const importState = {
        exportedApplications: [],
        exportedApplicationsWithEntities: {},
        exportedEntities: [],
        exportedDeployments: [],
    };
    const formattedApplications = formatApplicationsToExport(jdlObject.applications);
    importState.exportedApplications = formattedApplications;
    const entitiesPerApplicationMap = convert({
        jdlObject,
    });
    entitiesPerApplicationMap.forEach((jsonEntities, applicationName) => {
        const jdlApplication = jdlObject.getApplication(applicationName);
        const exportedJSONEntities = exportJSONEntities(jsonEntities, {
            applicationName,
            applicationType: jdlApplication.getConfigurationOptionValue(APPLICATION_TYPE),
            forSeveralApplications: true,
        });
        const exportedConfig = importState.exportedApplications.find(config => applicationName === config['generator-begcode'].baseName);
        const { 'generator-begcode': config, ...remaining } = exportedConfig;
        importState.exportedApplicationsWithEntities[applicationName] = {
            config,
            ...remaining,
            entities: exportedJSONEntities,
        };
        importState.exportedEntities = uniqBy([...importState.exportedEntities, ...exportedJSONEntities], 'name');
    });
    return importState;
}
function importDeployments(deployments) {
    return exportDeployments(deployments);
}
function exportJSONEntities(entities, configuration) {
    let baseName = configuration.applicationName;
    let applicationType = configuration.applicationType;
    if (configuration.application) {
        baseName = configuration.application[GENERATOR_BEGCODE].baseName;
        applicationType = configuration.application[GENERATOR_BEGCODE].applicationType;
    }
    return exportEntities({
        entities,
        application: {
            name: baseName,
            type: applicationType,
            forSeveralApplications: !!configuration.forSeveralApplications,
        },
    });
}
