import fs from 'fs';
import path from 'path';
import chalk from 'chalk';
import { upperFirst } from 'lodash-es';
import BaseApplicationGenerator from '../base-application/index.js';
import prompts from './prompts.js';
import { JHIPSTER_CONFIG_DIR } from '../generator-constants.js';
import { applicationTypes, reservedKeywords } from '../../jdl/jhipster/index.js';
import { GENERATOR_ENTITIES } from '../generator-list.js';
import { getDBTypeFromDBValue, hibernateSnakeCase } from '../server/support/index.js';
const { GATEWAY, MICROSERVICE } = applicationTypes;
const { isReservedClassName } = reservedKeywords;
export default class EntityGenerator extends BaseApplicationGenerator {
    name;
    application = {};
    constructor(args, options, features) {
        super(args, options, { unique: 'argument', ...features });
    }
    async beforeQueue() {
        if (!this.fromBlueprint) {
            await this.composeWithBlueprints();
        }
        if (!this.delegateToBlueprint) {
            await this.dependsOnBootstrapApplication();
        }
    }
    get initializing() {
        return this.asInitializingTaskGroup({
            async parseCommand() {
                await this.parseCurrentJHipsterCommand();
            },
            parseOptions() {
                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.entityData = {
                    name,
                    filename,
                    configExisted,
                    entityExisted,
                    configurationFileExists: this.fs.exists(this.destinationPath(filename)),
                };
                this._setupEntityOptions(this, this, this.entityData);
            },
            loadOptions() {
                if (this.options.db) {
                    this.entityConfig.databaseType = getDBTypeFromDBValue(this.options.db);
                    if (this.entityConfig.databaseType === 'sql') {
                        this.entityConfig.prodDatabaseType = this.options.db;
                        this.entityConfig.devDatabaseType = this.options.db;
                    }
                }
                if (this.options.skipServer !== undefined) {
                    this.entityConfig.skipServer = this.options.skipServer;
                }
                if (this.options.skipDbChangelog !== undefined) {
                    this.entityConfig.skipDbChangelog = this.options.skipDbChangelog;
                }
                if (this.options.skipClient !== undefined) {
                    this.entityConfig.skipClient = this.options.skipClient;
                }
            },
        });
    }
    get [BaseApplicationGenerator.INITIALIZING]() {
        return this.delegateTasksToBlueprint(() => this.initializing);
    }
    get prompting() {
        return this.asPromptingTaskGroup({
            askForMicroserviceJson: prompts.askForMicroserviceJson,
        });
    }
    get [BaseApplicationGenerator.PROMPTING]() {
        return this.delegateTasksToBlueprint(() => this.prompting);
    }
    get loading() {
        return this.asLoadingTaskGroup({
            isBuiltInEntity() {
                if (this.isBuiltInUser(this.entityData.name) || this.isBuiltInAuthority(this.entityData.name)) {
                    throw new Error(`Is not possible to override built in ${this.entityData.name}`);
                }
            },
            setupMicroServiceEntity({ application }) {
                const context = this.entityData;
                if (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 (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;
                    }
                }
            },
            loadEntitySpecificOptions({ application }) {
                this.entityData.skipClient = this.entityData.skipClient || this.entityConfig.skipClient;
                this.entityData.databaseType = this.entityData.databaseType || this.entityConfig.databaseType || application.databaseType;
            },
            validateEntityName() {
                const validation = this._validateEntityName(this.entityData.name);
                if (validation !== true) {
                    throw new Error(validation);
                }
            },
            bootstrapConfig({ application }) {
                const context = this.entityData;
                const entityName = context.name;
                if ([MICROSERVICE, GATEWAY].includes(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.LOADING]() {
        return this.delegateTasksToBlueprint(() => this.loading);
    }
    get postPreparing() {
        return this.asPostPreparingTaskGroup({
            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,
            async composeEntities() {
                await this.composeWithJHipster(GENERATOR_ENTITIES, {
                    generatorArgs: this.options.singleEntity ? [this.entityData.name] : [],
                    generatorOptions: {
                        skipDbChangelog: this.options.skipDbChangelog,
                        skipInstall: this.options.skipInstall,
                    },
                });
            },
        });
    }
    get [BaseApplicationGenerator.POST_PREPARING]() {
        return this.delegateTasksToBlueprint(() => this.postPreparing);
    }
    get end() {
        return {
            end() {
                this.log.log(chalk.bold.green(`Entity ${this.entityData.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.entityData.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);
    }
}
