/**
 * 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 path from 'node:path';
import chalk from 'chalk';
import { isFileStateModified } from 'mem-fs-editor/state';
import { clientFrameworkTypes } from "../../lib/jhipster/index.js";
import { mutateData } from "../../lib/utils/index.js";
import BaseApplicationGenerator from "../base-application/index.js";
import { createNeedleCallback } from "../base-core/support/index.js";
import { generateEntityClientEnumImports as getClientEnumImportsFormat } from "../client/support/index.js";
import { JAVA_WEBAPP_SOURCES_DIR } from "../index.js";
import { defaultLanguage } from "../languages/support/index.js";
import cleanupOldFilesTask from "./cleanup.js";
import { cleanupEntitiesFiles, postWriteEntitiesFiles, writeEntitiesFiles } from "./entity-files-angular.js";
import { writeFiles } from "./files-angular.js";
import { addEntitiesRoute, addIconImport, addItemToAdminMenu, addRoute, addToEntitiesMenu, isTranslatedAngularFile, translateAngularFilesTransform, } from "./support/index.js";
const { ANGULAR } = clientFrameworkTypes;
export class AngularApplicationGenerator extends BaseApplicationGenerator {
}
export default class AngularGenerator extends AngularApplicationGenerator {
    async beforeQueue() {
        if (!this.fromBlueprint) {
            await this.composeWithBlueprints();
        }
        await this.dependsOnBootstrap('angular');
        if (!this.delegateToBlueprint) {
            await this.dependsOnJHipster('jhipster:client:i18n');
            await this.dependsOnJHipster('client');
            await this.dependsOnJHipster('languages');
        }
    }
    get configuring() {
        return this.asConfiguringTaskGroup({
            migrateWebpackAndEsbuild({ control }) {
                if (control.isJhipsterVersionLessThan('9.0.0-alpha.0')) {
                    this.jhipsterConfig.clientBundler ??= 'webpack';
                }
                // @ts-expect-error renamed option
                if (this.jhipsterConfig.clientBundler === 'experimentalEsbuild') {
                    this.jhipsterConfig.clientBundler = 'esbuild';
                }
            },
        });
    }
    get [BaseApplicationGenerator.CONFIGURING]() {
        return this.delegateTasksToBlueprint(() => this.configuring);
    }
    get composing() {
        return this.asComposingTaskGroup({
            async composing() {
                await this.composeWithJHipster('jhipster:javascript-simple-application:eslint');
                await this.composeWithJHipster('jhipster:client:common');
                if (this.jhipsterConfigWithDefaults.websocket === 'spring-websocket') {
                    await this.composeWithJHipster('jhipster:client:encode-csrf-token');
                }
            },
        });
    }
    get [BaseApplicationGenerator.COMPOSING]() {
        return this.delegateTasksToBlueprint(() => this.composing);
    }
    get preparing() {
        return this.asPreparingTaskGroup({
            loadPackageJson({ application }) {
                this.loadNodeDependenciesFromPackageJson(application.nodeDependencies, this.fetchFromInstalledJHipster('angular', 'resources', 'package.json'));
            },
            applicationDefaults({ application, applicationDefaults }) {
                applicationDefaults({
                    __override__: true,
                    webappEnumerationsDir: app => `${app.clientSrcDir}app/entities/enumerations/`,
                    angularLocaleId: app => app.nativeLanguageDefinition.angularLocale ?? defaultLanguage.angularLocale,
                });
                application.prettierExtensions.push('html', 'css', 'scss');
                application.prettierFolders.push(application.clientBundlerWebpack ? 'webpack/**/' : 'build-plugins/**/');
                if (!application.backendTypeJavaAny && application.clientSrcDir !== JAVA_WEBAPP_SOURCES_DIR) {
                    // When we have a java backend, 'src/**' is already added by java:bootstrap
                    application.prettierFolders.push(`${application.clientSrcDir}**/`);
                }
            },
            async javaNodeBuildPaths({ application }) {
                application.javaNodeBuildPaths?.push('angular.json', 'tsconfig.json', 'tsconfig.app.json');
                if (application.clientBundlerWebpack) {
                    application.javaNodeBuildPaths?.push('webpack/');
                }
                else if (application.clientBundlerEsbuild) {
                    application.javaNodeBuildPaths?.push('build-plugins/');
                    if (application.enableI18nRTL) {
                        application.javaNodeBuildPaths?.push('postcss.conf.json');
                    }
                }
            },
            addNeedles({ source, application }) {
                source.addEntitiesToClient = param => {
                    const routeTemplatePath = `${param.application.clientSrcDir}app/entities/entity.routes.ts`;
                    const ignoreNonExistingRoute = chalk.yellow(`Route(s) not added to ${routeTemplatePath}.`);
                    const addRouteCallback = addEntitiesRoute(param);
                    this.editFile(routeTemplatePath, { ignoreNonExisting: ignoreNonExistingRoute }, addRouteCallback);
                    const filePath = `${application.clientSrcDir}app/layouts/navbar/navbar.html`;
                    const ignoreNonExisting = chalk.yellow('Reference to entities not added to menu.');
                    const editCallback = addToEntitiesMenu(param);
                    this.editFile(filePath, { ignoreNonExisting }, editCallback);
                    if (application.applicationTypeMicroservice) {
                        this.editFile(`${application.clientSrcDir}app/entities/entity-navbar-items.ts`, createNeedleCallback({
                            needle: 'add-entity-navbar',
                            contentToAdd: param.entities.map(entity => ({
                                contentToCheck: `route: '/${entity.entityPage}',`,
                                content: `{
  name: '${entity.entityAngularName}',
  route: '/${entity.entityPage}',${application.enableTranslation ?
                                    `
  translationKey: '${entity.entityTranslationKeyMenuPath}',`
                                    : ''}
  },`,
                            })),
                        }));
                    }
                };
                source.addAdminRoute = (args) => this.editFile(`${application.clientSrcDir}app/admin/admin.routes.ts`, addRoute({
                    needle: 'add-admin-route',
                    ...args,
                }));
                source.addItemToAdminMenu = (args) => {
                    this.editFile(`${application.clientSrcDir}app/layouts/navbar/navbar.html`, addItemToAdminMenu({
                        enableTranslation: application.enableTranslation,
                        jhiPrefix: application.jhiPrefix,
                        ...args,
                    }));
                    if (args.icon) {
                        source.addIconImport({ icon: args.icon });
                    }
                };
                source.addIconImport = args => {
                    const iconsPath = `${application.clientSrcDir}app/config/font-awesome-icons.ts`;
                    const ignoreNonExisting = this.ignoreNeedlesError && 'Icon imports not updated with icon';
                    this.editFile(iconsPath, { ignoreNonExisting }, addIconImport(args));
                };
                if (application.clientBundlerWebpack) {
                    source.addWebpackConfig = args => {
                        const webpackPath = `${application.clientRootDir}webpack/webpack.custom.js`;
                        const ignoreNonExisting = this.ignoreNeedlesError && 'Webpack configuration file not found';
                        this.editFile(webpackPath, { ignoreNonExisting }, createNeedleCallback({
                            needle: 'jhipster-needle-add-webpack-config',
                            contentToAdd: `${args.config},`,
                        }));
                    };
                }
                if (application.clientRootDir) {
                    // Overrides only works if added in root package.json
                    this.packageJson.merge({
                        overrides: {
                            'browser-sync': application.nodeDependencies['browser-sync'],
                            webpack: application.nodeDependencies.webpack,
                        },
                    });
                }
            },
        });
    }
    get [BaseApplicationGenerator.PREPARING]() {
        return this.delegateTasksToBlueprint(() => this.preparing);
    }
    get preparingEachEntity() {
        return this.asPreparingEachEntityTaskGroup({
            prepareEntity({ entity }) {
                const asAuthorities = (authorities) => authorities.length > 0 ? authorities.map(auth => `'${auth}'`).join(', ') : undefined;
                mutateData(entity, {
                    entityAngularAuthorities: asAuthorities(entity.entityAuthority?.split(',') ?? []),
                    entityAngularReadAuthorities: asAuthorities([
                        ...(entity.entityAuthority?.split(',') ?? []),
                        ...(entity.entityReadAuthority?.split(',') ?? []),
                    ]),
                });
                entity.generateEntityClientEnumImports = (fields) => getClientEnumImportsFormat(fields, ANGULAR);
            },
        });
    }
    get [BaseApplicationGenerator.PREPARING_EACH_ENTITY]() {
        return this.delegateTasksToBlueprint(() => this.preparingEachEntity);
    }
    get preparingEachEntityField() {
        return this.asPreparingEachEntityFieldTaskGroup({
            prepareField({ field }) {
                mutateData(field, {
                    fieldTsDefaultValue: ({ fieldTsDefaultValue, defaultValue, fieldTypeCharSequence, fieldTypeTimed }) => {
                        let returnValue;
                        if (fieldTsDefaultValue !== undefined || defaultValue !== undefined) {
                            let fieldDefaultValue;
                            if (fieldTsDefaultValue === undefined) {
                                fieldDefaultValue = defaultValue;
                            }
                            else {
                                fieldDefaultValue = fieldTsDefaultValue;
                            }
                            fieldDefaultValue = String(fieldDefaultValue).replace(/'/g, String.raw `\'`);
                            if (fieldTypeCharSequence) {
                                returnValue = `'${fieldDefaultValue}'`;
                            }
                            else if (fieldTypeTimed) {
                                returnValue = `dayjs('${fieldDefaultValue}')`;
                            }
                            else {
                                returnValue = fieldDefaultValue;
                            }
                        }
                        return returnValue;
                    },
                    fieldValidateRulesPatternAngular: ({ fieldValidateRulesPattern }) => fieldValidateRulesPattern?.replace(/\\\\/g, '\\').replace(/\\/g, '\\\\').replace(/'/g, "\\'"),
                });
            },
        });
    }
    get [BaseApplicationGenerator.PREPARING_EACH_ENTITY_FIELD]() {
        return this.delegateTasksToBlueprint(() => this.preparingEachEntityField);
    }
    get default() {
        return this.asDefaultTaskGroup({
            queueTranslateTransform({ application }) {
                const { enableTranslation, jhiPrefix } = application;
                this.queueTransformStream({
                    name: 'translating angular application',
                    filter: file => isFileStateModified(file) && file.path.startsWith(this.destinationPath()) && isTranslatedAngularFile(file),
                    refresh: false,
                }, translateAngularFilesTransform(application.getWebappTranslation, { enableTranslation, jhiPrefix }));
            },
        });
    }
    get [BaseApplicationGenerator.DEFAULT]() {
        return this.delegateTasksToBlueprint(() => this.default);
    }
    get writing() {
        return this.asWritingTaskGroup({
            async cleanup({ application, control }) {
                await control.cleanupFiles({
                    '8.6.1': ['.eslintrc.json', '.eslintignore'],
                    '8.7.4': [`${application.clientSrcDir}app/app.constants.ts`],
                    '9.0.0-alpha.0': [
                        // Try to remove possibles old eslint config files
                        'eslint.config.js',
                        'eslint.config.mjs',
                        [
                            application.clientBundlerEsbuild,
                            `${application.clientRootDir}build-plugins/define-esbuild.mjs`,
                            `${application.clientRootDir}build-plugins/i18n-esbuild.mjs`,
                        ],
                        [
                            !application.microfrontend || !application.applicationTypeMicroservice,
                            `${application.clientSrcDir}app/entities/entity-navbar-items.ts`,
                        ],
                        `${application.clientSrcDir}app/config/uib-pagination.config.ts`,
                    ],
                    '9.1.1': [[application.enableTranslation, `${application.clientSrcDir}app/shared/language/translation.module.ts`]],
                    '8.0.0-beta.1': [
                        `${application.clientRootDir}jest.js`,
                        `${application.clientSrcDir}app/shared/shared.module.ts.ejs`,
                        `${application.clientSrcDir}app/core/interceptor/index.ts.ejs`,
                        `${application.clientSrcDir}default-test-providers.ts.ejs`,
                        `${application.clientSrcDir}app/config/error.constants.ts`,
                        `${application.clientSrcDir}app/config/authority.constants.ts`,
                    ],
                    '8.0.0-beta.4': [
                        `${application.clientSrcDir}app/admin/user-management/user-management.route.ts`,
                        `${application.clientSrcDir}app/admin/user-management/user-management.model.ts`,
                        `${application.clientSrcDir}app/admin/user-management/list/user-management.html`,
                        `${application.clientSrcDir}app/admin/user-management/list/user-management.spec.ts`,
                        `${application.clientSrcDir}app/admin/user-management/list/user-management.ts`,
                        `${application.clientSrcDir}app/admin/user-management/detail/user-management-detail.html`,
                        `${application.clientSrcDir}app/admin/user-management/detail/user-management-detail.spec.ts`,
                        `${application.clientSrcDir}app/admin/user-management/detail/user-management-detail.ts`,
                        `${application.clientSrcDir}app/admin/user-management/update/user-management-update.html`,
                        `${application.clientSrcDir}app/admin/user-management/update/user-management-update.spec.ts`,
                        `${application.clientSrcDir}app/admin/user-management/update/user-management-update.ts`,
                        `${application.clientSrcDir}app/admin/user-management/delete/user-management-delete-dialog.html`,
                        `${application.clientSrcDir}app/admin/user-management/delete/user-management-delete-dialog.spec.ts`,
                        `${application.clientSrcDir}app/admin/user-management/delete/user-management-delete-dialog.ts`,
                        `${application.clientSrcDir}app/admin/user-management/service/user-management.service.spec.ts`,
                        `${application.clientSrcDir}app/admin/user-management/service/user-management.service.ts`,
                    ],
                });
            },
            cleanupOldFilesTask,
            writeFiles,
        });
    }
    get [BaseApplicationGenerator.WRITING]() {
        return this.delegateTasksToBlueprint(() => this.writing);
    }
    get writingEntities() {
        return this.asWritingEntitiesTaskGroup({
            cleanupEntitiesFiles,
            writeEntitiesFiles,
        });
    }
    get [BaseApplicationGenerator.WRITING_ENTITIES]() {
        return this.delegateTasksToBlueprint(() => this.writingEntities);
    }
    get postWriting() {
        return this.asPostWritingTaskGroup({
            addPrettierConfig({ application, source }) {
                source.mergePrettierConfig?.({
                    overrides: [{ files: path.join(application.clientSrcDir, '**/*.html'), options: { parser: 'angular' } }],
                });
            },
            clientBundler({ application, source }) {
                const { clientBundlerEsbuild, enableTranslation, nodeDependencies } = application;
                if (clientBundlerEsbuild) {
                    source.mergeClientPackageJson({
                        devDependencies: {
                            '@angular-builders/custom-esbuild': null,
                            '@angular/build': null,
                            tinyglobby: null,
                            ...(enableTranslation ? { '@types/folder-hash': null, 'folder-hash': null, deepmerge: null } : {}),
                        },
                    });
                }
                else {
                    source.mergeClientPackageJson({
                        dependencies: enableTranslation ?
                            {
                                '@ngx-translate/http-loader': null,
                            }
                            : {},
                        devDependencies: {
                            '@angular-builders/custom-webpack': null,
                            'browser-sync-webpack-plugin': null,
                            'copy-webpack-plugin': null,
                            'webpack-bundle-analyzer': null,
                            'webpack-merge': null,
                            'webpack-notifier': null,
                            ...(enableTranslation ? { 'folder-hash': null, 'merge-jsons-webpack-plugin': null } : {}),
                        },
                        overrides: {
                            'browser-sync': nodeDependencies['browser-sync'],
                            webpack: nodeDependencies.webpack,
                        },
                    });
                }
            },
            addWebsocketDependencies({ application, source }) {
                const { authenticationTypeSession, communicationSpringWebsocket, nodeDependencies } = application;
                const dependencies = {};
                if (communicationSpringWebsocket) {
                    if (authenticationTypeSession) {
                        dependencies['ngx-cookie-service'] = nodeDependencies['ngx-cookie-service'];
                    }
                    source.mergeClientPackageJson({
                        dependencies: {
                            'sockjs-client': nodeDependencies['sockjs-client'],
                            '@stomp/rx-stomp': nodeDependencies['@stomp/rx-stomp'],
                            ...dependencies,
                        },
                        devDependencies: {
                            '@types/sockjs-client': nodeDependencies['@types/sockjs-client'],
                        },
                    });
                }
            },
            sonar({ application, source }) {
                const { clientDistDir, clientSrcDir, clientI18nDir, temporaryDir } = application;
                source.addSonarProperties?.([
                    { key: 'sonar.test.inclusions', value: `${clientSrcDir}app/**/*.spec.ts`, valueSep: ', ' },
                    { key: 'sonar.testExecutionReportPaths', value: `${temporaryDir}test-results/TESTS-results-sonar-vitest.xml` },
                    { key: 'sonar.javascript.lcov.reportPaths', value: `${temporaryDir}test-results/lcov.info` },
                    {
                        key: 'sonar.exclusions',
                        value: `${clientSrcDir}content/**/*.*, ${clientI18nDir}*.ts, ${clientDistDir}**/*.*`,
                        valueSep: ', ',
                    },
                ]);
            },
        });
    }
    get [BaseApplicationGenerator.POST_WRITING]() {
        return this.delegateTasksToBlueprint(() => this.postWriting);
    }
    get postWritingEntities() {
        return this.asPostWritingEntitiesTaskGroup({
            postWriteEntitiesFiles,
        });
    }
    get [BaseApplicationGenerator.POST_WRITING_ENTITIES]() {
        return this.delegateTasksToBlueprint(() => this.postWritingEntities);
    }
    get end() {
        return this.asEndTaskGroup({
            end({ application }) {
                this.log.ok(`Angular ${application.nodeDependencies['@angular/common']} application generated successfully.`);
                this.log.log(chalk.green(`  Start your Webpack development server with:
  ${chalk.yellow.bold(`${application.nodePackageManager} start`)}
`));
            },
        });
    }
    get [BaseApplicationGenerator.END]() {
        return this.delegateTasksToBlueprint(() => this.end);
    }
}
