/**
 * 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 { upperFirst } from 'lodash-es';
import { clientFrameworkTypes, testFrameworkTypes } from "../../lib/jhipster/index.js";
import { mutateData } from "../../lib/utils/object.js";
import BaseApplicationGenerator from "../base-application/index.js";
import { createNeedleCallback } from "../base-core/support/index.js";
import { isReservedTypescriptKeyword } from "../javascript-simple-application/support/reserved-words.js";
import { addEnumerationFiles } from "./entity-files.js";
import { writeFiles as writeCommonFiles } from "./files-common.js";
import { askForClientTheme, askForClientThemeVariant } from "./prompts.js";
import { filterEntitiesAndPropertiesForClient } from "./support/filter-entities.js";
const { ANGULAR, NO: CLIENT_FRAMEWORK_NO } = clientFrameworkTypes;
const { CYPRESS } = testFrameworkTypes;
export class ClientApplicationGenerator extends BaseApplicationGenerator {
}
export default class ClientGenerator extends ClientApplicationGenerator {
    constructor(args, options, features) {
        super(args, options, { skipLoadCommand: true, ...features });
    }
    async beforeQueue() {
        if (!this.fromBlueprint) {
            await this.composeWithBlueprints();
        }
        if (!this.delegateToBlueprint) {
            await this.dependsOnBootstrap('client');
            await this.dependsOnJHipster('common');
        }
    }
    get prompting() {
        return this.asPromptingTaskGroup({
            askForClientTheme,
            askForClientThemeVariant,
        });
    }
    get [ClientApplicationGenerator.PROMPTING]() {
        return this.delegateTasksToBlueprint(() => this.prompting);
    }
    get configuring() {
        return this.asConfiguringTaskGroup({
            applyNoFramework() {
                const { clientFramework } = this.jhipsterConfigWithDefaults;
                if (clientFramework === CLIENT_FRAMEWORK_NO) {
                    this.jhipsterConfig.skipClient = true;
                    this.cancelCancellableTasks();
                }
            },
            mergeTestConfig() {
                if (this.jhipsterConfig.clientTestFrameworks) {
                    this.jhipsterConfig.testFrameworks = [
                        ...new Set([...(this.jhipsterConfig.testFrameworks ?? []), ...this.jhipsterConfig.clientTestFrameworks]),
                    ];
                    delete this.jhipsterConfig.clientTestFrameworks;
                }
            },
            upgradeAngular() {
                if (this.jhipsterConfig.clientFramework === 'angularX') {
                    this.jhipsterConfig.clientFramework = ANGULAR;
                }
            },
            configureDevServerPort() {
                if (this.jhipsterConfig.devServerPort !== undefined || this.jhipsterConfig.applicationIndex === undefined)
                    return;
                const { applicationIndex, devServerPort } = this.jhipsterConfigWithDefaults;
                this.jhipsterConfig.devServerPort = devServerPort + applicationIndex;
            },
        });
    }
    get [ClientApplicationGenerator.CONFIGURING]() {
        return this.delegateTasksToBlueprint(() => this.configuring);
    }
    get composing() {
        return this.asComposingTaskGroup({
            async composing() {
                const { clientFramework, testFrameworks } = this.jhipsterConfigWithDefaults;
                if (['angular', 'react', 'vue'].includes(clientFramework)) {
                    await this.composeWithJHipster(clientFramework);
                }
                if (Array.isArray(testFrameworks) && testFrameworks.includes(CYPRESS)) {
                    await this.composeWithJHipster('cypress');
                }
            },
        });
    }
    get [ClientApplicationGenerator.COMPOSING]() {
        return this.delegateTasksToBlueprint(() => this.composing);
    }
    get preparing() {
        return this.asPreparingTaskGroup({
            loadPackageJson({ application }) {
                // Load common client package.json into packageJson
                this.loadNodeDependenciesFromPackageJson(application.nodeDependencies, this.fetchFromInstalledJHipster('client', 'resources', 'package.json'));
            },
            addExternalResource({ application, source }) {
                if (!application.clientFrameworkBuiltIn) {
                    return;
                }
                source.addExternalResourceToRoot = ({ resource, comment }) => this.editFile(`${application.clientSrcDir}index.html`, createNeedleCallback({
                    needle: 'add-resources-to-root',
                    contentToAdd: [comment ? `<!-- ${comment} -->` : undefined, resource].filter(i => i).join('\n'),
                }));
            },
        });
    }
    get [ClientApplicationGenerator.PREPARING]() {
        return this.delegateTasksToBlueprint(() => this.preparing);
    }
    get postPreparing() {
        return this.asPostPreparingTaskGroup({
            microfrontend({ application }) {
                if (application.microfrontends) {
                    if (application.exposeMicrofrontend) {
                        application.microfrontends.unshift({
                            baseName: application.baseName,
                            endpointPrefix: '',
                        });
                    }
                    application.microfrontends.forEach(microfrontend => {
                        const { baseName } = microfrontend;
                        mutateData(microfrontend, {
                            lowercaseBaseName: baseName.toLowerCase(),
                            moduleFederationName: ({ lowercaseBaseName }) => lowercaseBaseName.replaceAll('-', '_'),
                            capitalizedBaseName: upperFirst(baseName),
                            endpointPrefix: `services/${baseName.toLowerCase()}`,
                        });
                    });
                }
            },
        });
    }
    get [BaseApplicationGenerator.POST_PREPARING]() {
        return this.postPreparing;
    }
    get preparingEachEntity() {
        return this.asPreparingEachEntityTaskGroup({
            preparing({ entityName }) {
                if (isReservedTypescriptKeyword(entityName)) {
                    throw new Error(`The entity name "${entityName}" is a reserved TypeScript keyword. It may cause issues in your application.`);
                }
            },
        });
    }
    get [ClientApplicationGenerator.PREPARING_EACH_ENTITY]() {
        return this.delegateTasksToBlueprint(() => this.preparingEachEntity);
    }
    get preparingEachEntityField() {
        return this.asPreparingEachEntityFieldTaskGroup({
            preparing({ entity, field }) {
                if (isReservedTypescriptKeyword(field.fieldName)) {
                    throw new Error(`The field name "${field.fieldName}" in entity "${entity.name}" is a reserved TypeScript keyword.`);
                }
            },
        });
    }
    get [ClientApplicationGenerator.PREPARING_EACH_ENTITY_FIELD]() {
        return this.delegateTasksToBlueprint(() => this.preparingEachEntityField);
    }
    get preparingEachEntityRelationship() {
        return this.asPreparingEachEntityRelationshipTaskGroup({
            preparing({ entity, relationship }) {
                if (isReservedTypescriptKeyword(relationship.relationshipName)) {
                    throw new Error(`The relationship name "${relationship.relationshipName}" in entity "${entity.name}" is a reserved TypeScript keyword.`);
                }
            },
        });
    }
    get [ClientApplicationGenerator.PREPARING_EACH_ENTITY_RELATIONSHIP]() {
        return this.delegateTasksToBlueprint(() => this.preparingEachEntityRelationship);
    }
    get writing() {
        return this.asWritingTaskGroup({
            async cleanup({ application, control }) {
                await control.cleanupFiles({
                    '8.7.4': [`${application.clientSrcDir}swagger-ui/dist/images/throbber.gif`],
                });
            },
            webappFakeDataSeed({ application: { clientFramework } }) {
                this.resetEntitiesFakeData(clientFramework);
            },
            writeCommonFiles,
        });
    }
    get [ClientApplicationGenerator.WRITING]() {
        return this.delegateTasksToBlueprint(() => this.writing);
    }
    get writingEntities() {
        return this.asWritingEntitiesTaskGroup({
            async writeEnumerationFiles({ application, entities }) {
                if (!application.webappEnumerationsDir || !application.clientFrameworkBuiltIn) {
                    return;
                }
                for (const entity of (application.filterEntitiesAndPropertiesForClient ?? filterEntitiesAndPropertiesForClient)(entities)) {
                    await addEnumerationFiles.call(this, { application, entity });
                }
            },
        });
    }
    get [ClientApplicationGenerator.WRITING_ENTITIES]() {
        return this.delegateTasksToBlueprint(() => this.writingEntities);
    }
    get postWriting() {
        return this.asPostWritingTaskGroup({
            packageJsonScripts({ application }) {
                if (!application.clientFrameworkBuiltIn) {
                    return;
                }
                const packageJsonStorage = this.createStorage(this.destinationPath(application.clientRootDir, 'package.json'));
                const scriptsStorage = packageJsonStorage.createStorage('scripts');
                const devDependencies = packageJsonStorage.createStorage('devDependencies');
                devDependencies.set('wait-on', application.nodeDependencies['wait-on']);
                devDependencies.set('concurrently', application.nodeDependencies.concurrently);
                if (application.clientFrameworkReact) {
                    scriptsStorage.set('ci:frontend:test', 'npm run webapp:build:$npm_package_config_default_environment && npm run test');
                }
                else {
                    scriptsStorage.set('ci:frontend:build', 'npm run webapp:build:$npm_package_config_default_environment');
                    scriptsStorage.set('ci:frontend:test', 'npm run ci:frontend:build && npm test');
                }
                if (application.clientRootDir) {
                    // Add scripts to map to client package.json
                    this.packageJson.merge({
                        scripts: {
                            'webapp:build': `npm run -w ${application.clientRootDir} webapp:build`,
                            'ci:frontend:test': `npm run -w ${application.clientRootDir} ci:frontend:test`,
                        },
                    });
                    const clientWorkspace = application.clientRootDir.slice(0, -1);
                    const packageJson = this.packageJson.createProxy();
                    const workspaces = packageJson.workspaces;
                    if (!workspaces?.includes(clientWorkspace)) {
                        packageJson.workspaces = [...(workspaces ?? []), clientWorkspace];
                    }
                }
            },
            microfrontend({ application, source }) {
                if (!application.microfrontend || !application.clientFrameworkBuiltIn || !application.clientBundlerWebpack) {
                    return;
                }
                if (application.clientFrameworkAngular) {
                    const conditional = application.applicationTypeMicroservice ? "targetOptions.target === 'serve' ? {} : " : '';
                    source.addWebpackConfig({
                        config: `${conditional}require('./webpack.microfrontend')(config, options, targetOptions)`,
                    });
                }
                else if (application.clientFrameworkVue || application.clientFrameworkReact) {
                    source.addWebpackConfig({ config: "require('./webpack.microfrontend')({ serve: options.env.WEBPACK_SERVE })" });
                }
                else {
                    throw new Error(`Client framework ${application.clientFramework} doesn't support microfrontends`);
                }
            },
        });
    }
    get [ClientApplicationGenerator.POST_WRITING]() {
        return this.delegateTasksToBlueprint(() => this.postWriting);
    }
}
