import fs from 'fs';
import path from 'path';
import chalk from 'chalk';
import _ from 'lodash';
import BaseApplicationGenerator from '../base-application/index.mjs';
import prompts from './prompts.mjs';
import { JHIPSTER_CONFIG_DIR } from '../generator-constants.mjs';
import { applicationTypes, clientFrameworkTypes, getConfigWithDefaults, reservedKeywords } from '../../jdl/jhipster/index.mjs';
import { GENERATOR_ENTITIES, GENERATOR_ENTITY } from '../generator-list.mjs';
import { removeFieldsWithNullishValues } from '../base/support/index.mjs';
import { getDBTypeFromDBValue, hibernateSnakeCase, loadDerivedPlatformConfig, loadDerivedServerConfig, loadServerConfig, } from '../server/support/index.mjs';
import { loadAppConfig, loadDerivedAppConfig } from '../app/support/index.mjs';
import { loadClientConfig, loadDerivedClientConfig } from '../client/support/index.mjs';
import { loadLanguagesConfig } from '../languages/support/index.mjs';
import command from './command.mjs';
const { GATEWAY, MICROSERVICE } = applicationTypes;
const { NO: CLIENT_FRAMEWORK_NO } = clientFrameworkTypes;
const { isReservedClassName } = reservedKeywords;
export default class EntityGenerator extends BaseApplicationGenerator {
    constructor(args, options, features) {
        super(args, options, { unique: 'argument', ...features });
    }
    async beforeQueue() {
        this.parseJHipsterArguments(command.arguments);
        const name = _.upperFirst(this.name).replace('.json', '');
        this.entityStorage = this.getEntityConfig(name, true);
        this.entityConfig = this.entityStorage.createProxy();
        const configExisted = this.entityStorage.existed;
        const filename = path.join(JHIPSTER_CONFIG_DIR, `${name}.json`);
        const entityExisted = fs.existsSync(this.destinationPath(filename));
        this.jhipsterConfig.entities = [...(this.jhipsterConfig.entities ?? []), name];
        this.context = {
            name,
            filename,
            configExisted,
            entityExisted,
            configurationFileExists: this.fs.exists(this.destinationPath(filename)),
        };
        if (!this.fromBlueprint) {
            await this.composeWithBlueprints(GENERATOR_ENTITY, {
                generatorOptions: {
                    entityExisted,
                    configExisted,
                    arguments: [name],
                },
            });
        }
        this._setupEntityOptions(this, this, this.context);
    }
    get initializing() {
        return {
            askForMicroserviceJson: prompts.askForMicroserviceJson,
            loadSharedConfig() {
                this.application = {};
                loadAppConfig({
                    config: this.jhipsterConfigWithDefaults,
                    application: this.application,
                    useVersionPlaceholders: this.useVersionPlaceholders,
                });
                loadClientConfig({ config: this.jhipsterConfigWithDefaults, application: this.application });
                loadLanguagesConfig({ application: this.application, config: this.jhipsterConfigWithDefaults });
                loadServerConfig({
                    config: getConfigWithDefaults({
                        ...removeFieldsWithNullishValues(this.jhipsterConfig),
                        ...removeFieldsWithNullishValues(this.microserviceConfig ?? {}),
                    }),
                    application: this.application,
                });
                loadDerivedAppConfig({ application: this.application });
                loadDerivedClientConfig({ application: this.application });
                loadDerivedServerConfig({ application: this.application });
                loadDerivedPlatformConfig({ application: this.application });
            },
            isBuiltInEntity() {
                if (this.isBuiltInUser(this.context.name) || this.isBuiltInAuthority(this.context.name)) {
                    throw new Error(`Is not possible to override built in ${this.context.name}`);
                }
            },
            setupMicroServiceEntity() {
                const context = this.context;
                if (this.application.applicationType === MICROSERVICE) {
                    context.microserviceName = this.entityConfig.microserviceName = this.jhipsterConfig.baseName;
                    if (!this.entityConfig.clientRootFolder) {
                        context.clientRootFolder = this.entityConfig.clientRootFolder = this.entityConfig.microserviceName;
                    }
                }
                else if (this.application.applicationType === GATEWAY) {
                    context.useMicroserviceJson = !!this.entityConfig.microservicePath;
                    if (context.useMicroserviceJson) {
                        context.microserviceFileName = this.destinationPath(this.entityConfig.microservicePath, context.filename);
                        context.useConfigurationFile = true;
                        this.log.verboseInfo(`\nThe entity ${context.name} is being updated.\n`);
                        try {
                            this.microserviceConfig = this.fs.readJSON(context.microserviceFileName);
                            if (this.microserviceConfig) {
                                this.entityStorage.set(this.microserviceConfig);
                            }
                        }
                        catch (err) {
                            this.log.debug('Error:', err);
                            throw new Error(`The entity configuration file could not be read! ${err}`, { cause: err });
                        }
                    }
                    if (this.entityConfig.clientRootFolder === undefined) {
                        context.clientRootFolder = this.entityConfig.clientRootFolder = context.skipUiGrouping
                            ? ''
                            : this.entityConfig.microserviceName;
                    }
                    if (this.jhipsterConfig.applications && !this.entityConfig.skipClient) {
                        const remoteConfig = this.jhipsterConfig.applications[this.entityConfig.microserviceName];
                        if (remoteConfig && remoteConfig.clientFramework && remoteConfig.clientFramework !== CLIENT_FRAMEWORK_NO) {
                            this.entityConfig.skipClient = true;
                        }
                    }
                }
            },
            loadOptions() {
                const context = this.context;
                if (this.options.db) {
                    context.databaseType = getDBTypeFromDBValue(this.options.db);
                    context.prodDatabaseType = this.options.db;
                    context.devDatabaseType = this.options.db;
                }
                context.skipServer = context.skipServer || this.options.skipServer;
                context.skipDbChangelog = context.skipDbChangelog || this.options.skipDbChangelog;
                context.skipClient = context.skipClient || this.options.skipClient;
            },
            loadEntitySpecificOptions() {
                this.context.skipClient = this.context.skipClient || this.entityConfig.skipClient;
                this.context.databaseType = this.context.databaseType || this.entityConfig.databaseType || this.jhipsterConfig.databaseType;
            },
            validateEntityName() {
                const validation = this._validateEntityName(this.context.name);
                if (validation !== true) {
                    throw new Error(validation);
                }
            },
            bootstrapConfig() {
                const context = this.context;
                const entityName = context.name;
                if ([MICROSERVICE, GATEWAY].includes(this.application.applicationType)) {
                    if (this.entityConfig.databaseType === undefined) {
                        this.entityConfig.databaseType = context.databaseType;
                    }
                }
                context.useConfigurationFile = context.configurationFileExists || context.useConfigurationFile;
                if (context.configurationFileExists) {
                    this.log.log(chalk.green(`\nFound the ${context.filename} configuration file, entity can be automatically generated!\n`));
                }
                this.entityStorage.defaults({ fields: [], relationships: [] });
                if (!context.useConfigurationFile) {
                    this.log.verboseInfo(`\nThe entity ${entityName} is being created.\n`);
                }
            },
        };
    }
    get [BaseApplicationGenerator.INITIALIZING]() {
        return this.delegateTasksToBlueprint(() => this.initializing);
    }
    get prompting() {
        return {
            askForUpdate: prompts.askForUpdate,
            askForFields: prompts.askForFields,
            askForFieldsToRemove: prompts.askForFieldsToRemove,
            askForRelationships: prompts.askForRelationships,
            askForRelationsToRemove: prompts.askForRelationsToRemove,
            askForService: prompts.askForService,
            askForDTO: prompts.askForDTO,
            askForFiltering: prompts.askForFiltering,
            askForReadOnly: prompts.askForReadOnly,
            askForPagination: prompts.askForPagination,
        };
    }
    get [BaseApplicationGenerator.PROMPTING]() {
        return this.delegateTasksToBlueprint(() => this.prompting);
    }
    get composing() {
        return this.asComposingTaskGroup({
            async composeEntities() {
                await this.composeWithJHipster(GENERATOR_ENTITIES, {
                    generatorArgs: this.options.singleEntity ? [this.context.name] : [],
                    generatorOptions: {
                        skipDbChangelog: this.options.skipDbChangelog,
                        skipInstall: this.options.skipInstall,
                    },
                });
            },
        });
    }
    get [BaseApplicationGenerator.COMPOSING]() {
        return this.delegateTasksToBlueprint(() => this.composing);
    }
    get end() {
        return {
            end() {
                this.log.log(chalk.bold.green(`Entity ${this.context.entityNameCapitalized} generated successfully.`));
            },
        };
    }
    get [BaseApplicationGenerator.END]() {
        return this.delegateTasksToBlueprint(() => this.end);
    }
    _setupEntityOptions(generator, context = generator, dest = context) {
        dest.regenerate = context.options.regenerate;
        if (context.options.skipCheckLengthOfIdentifier !== undefined) {
            this.entityConfig.skipCheckLengthOfIdentifier = context.options.skipCheckLengthOfIdentifier;
        }
        if (context.options.angularSuffix !== undefined) {
            this.entityConfig.angularJSSuffix = context.options.angularSuffix;
        }
        if (context.options.skipUiGrouping !== undefined) {
            this.entityConfig.skipUiGrouping = context.options.skipUiGrouping;
        }
        if (context.options.clientRootFolder !== undefined) {
            if (this.entityConfig.skipUiGrouping) {
                this.warn('Ignoring client-root-folder due to skip-ui-grouping configuration');
            }
            else {
                this.entityConfig.clientRootFolder = context.options.clientRootFolder;
            }
        }
        if (context.options.skipClient !== undefined) {
            this.entityConfig.skipClient = context.options.skipClient;
        }
        if (context.options.skipServer !== undefined) {
            this.entityConfig.skipServer = context.options.skipServer;
        }
        if (context.options.tableName) {
            this.entityConfig.entityTableName = hibernateSnakeCase(context.options.tableName);
        }
    }
    _validateEntityName(entityName) {
        if (!/^([a-zA-Z0-9]*)$/.test(entityName)) {
            return 'The entity name must be alphanumeric only';
        }
        if (/^[0-9].*$/.test(entityName)) {
            return 'The entity name cannot start with a number';
        }
        if (entityName === '') {
            return 'The entity name cannot be empty';
        }
        if (entityName.indexOf('Detail', entityName.length - 'Detail'.length) !== -1) {
            return "The entity name cannot end with 'Detail'";
        }
        if (!this.context.skipServer && isReservedClassName(entityName)) {
            return 'The entity name cannot contain a Java or JHipster reserved keyword';
        }
        return true;
    }
    isBuiltInUser(entityName) {
        return this.generateBuiltInUserEntity && this.isUserEntity(entityName);
    }
    isUserEntity(entityName) {
        return _.upperFirst(entityName) === 'User';
    }
    isAuthorityEntity(entityName) {
        return _.upperFirst(entityName) === 'Authority';
    }
    isBuiltInAuthority(entityName) {
        return this.generateBuiltInAuthorityEntity && this.isAuthorityEntity(entityName);
    }
}
