import { rm } from 'node:fs/promises';
import chalk from 'chalk';
import { camelCase, snakeCase, upperFirst } from 'lodash-es';
import BaseGenerator from '../base-application/index.js';
import { PRIORITY_NAMES_LIST as BASE_PRIORITY_NAMES_LIST } from '../base/priorities.js';
import * as GENERATOR_LIST from '../generator-list.js';
import { packageJson } from '../../lib/index.js';
import { BLUEPRINT_API_VERSION, NODE_VERSION } from '../generator-constants.js';
import { files, generatorFiles } from './files.js';
import { ADDITIONAL_SUB_GENERATORS, ALL_GENERATORS, ALL_PRIORITIES, DYNAMIC, GENERATE_SNAPSHOTS, GENERATORS, LINK_JHIPSTER_DEPENDENCY, LOCAL_BLUEPRINT_OPTION, PRIORITIES, SUB_GENERATORS, WRITTEN, allGeneratorsConfig, defaultConfig, defaultSubGeneratorConfig, prompts, requiredConfig, subGeneratorPrompts, } from './constants.js';
const { GENERATOR_PROJECT_NAME, GENERATOR_INIT } = GENERATOR_LIST;
const defaultPublishedFiles = ['generators', '!**/__*', '!**/*.snap', '!**/*.spec.?(c|m)js'];
export default class extends BaseGenerator {
    application;
    recreatePackageLock;
    skipWorkflows;
    ignoreExistingGenerators;
    gitDependency;
    async _beforeQueue() {
        if (!this.fromBlueprint) {
            await this.composeWithBlueprints();
        }
        if (!this.delegateToBlueprint) {
            await this.dependsOnJHipster(GENERATOR_PROJECT_NAME);
        }
    }
    get initializing() {
        return this.asInitializingTaskGroup({
            async loadOptions() {
                if (this[ALL_GENERATORS]) {
                    this.config.set(allGeneratorsConfig());
                }
                if (this.options.defaults) {
                    this.config.defaults(defaultConfig({ config: this.jhipsterConfig }));
                }
            },
        });
    }
    get [BaseGenerator.INITIALIZING]() {
        return this.delegateTasksToBlueprint(() => this.initializing);
    }
    get prompting() {
        return this.asPromptingTaskGroup({
            async prompting() {
                await this.prompt(prompts(this), this.config);
            },
            async eachSubGenerator() {
                const { localBlueprint } = this.jhipsterConfig;
                const { [ALL_PRIORITIES]: allPriorities } = this.options;
                const subGenerators = this.config.get(SUB_GENERATORS) || [];
                for (const subGenerator of subGenerators) {
                    const subGeneratorStorage = this.getSubGeneratorStorage(subGenerator);
                    if (allPriorities) {
                        subGeneratorStorage.defaults({ [PRIORITIES]: BASE_PRIORITY_NAMES_LIST });
                    }
                    await this.prompt(subGeneratorPrompts({ subGenerator, localBlueprint, options: this.options }), subGeneratorStorage);
                }
            },
            async eachAdditionalSubGenerator() {
                const { localBlueprint } = this.jhipsterConfig;
                const { [ALL_PRIORITIES]: allPriorities } = this.options;
                const additionalSubGenerators = this.config.get(ADDITIONAL_SUB_GENERATORS) || '';
                for (const subGenerator of additionalSubGenerators
                    .split(',')
                    .map(sub => sub.trim())
                    .filter(Boolean)) {
                    const subGeneratorStorage = this.getSubGeneratorStorage(subGenerator);
                    if (allPriorities) {
                        subGeneratorStorage.defaults({ [PRIORITIES]: BASE_PRIORITY_NAMES_LIST });
                    }
                    await this.prompt(subGeneratorPrompts({ subGenerator, localBlueprint, additionalSubGenerator: true }), subGeneratorStorage);
                }
            },
        });
    }
    get [BaseGenerator.PROMPTING]() {
        return this.delegateTasksToBlueprint(() => this.prompting);
    }
    get configuring() {
        return this.asConfiguringTaskGroup({
            requiredConfig() {
                this.config.defaults(requiredConfig());
            },
            conditionalConfig() {
                if (this.jhipsterConfig[LOCAL_BLUEPRINT_OPTION]) {
                    this.config.defaults({
                        [DYNAMIC]: true,
                        js: false,
                    });
                }
            },
        });
    }
    get [BaseGenerator.CONFIGURING]() {
        return this.delegateTasksToBlueprint(() => this.configuring);
    }
    get composing() {
        return this.asComposingTaskGroup({
            storeCurrentVersion() {
                this.storeCurrentJHipsterVersion();
            },
            async compose() {
                if (this.jhipsterConfig[LOCAL_BLUEPRINT_OPTION])
                    return;
                const initGenerator = await this.composeWithJHipster(GENERATOR_INIT, { generatorOptions: { packageJsonType: 'module' } });
                initGenerator.generateReadme = false;
            },
        });
    }
    get [BaseGenerator.COMPOSING]() {
        return this.delegateTasksToBlueprint(() => this.composing);
    }
    get loading() {
        return this.asLoadingTaskGroup({
            async createContext() {
                this.application = { ...defaultConfig(), ...this.config.getAll() };
                await this.loadCurrentJHipsterCommandConfig(this.application);
            },
            async load() {
                this.application.packagejs = packageJson;
            },
        });
    }
    get [BaseGenerator.LOADING]() {
        return this.delegateTasksToBlueprint(() => this.loading);
    }
    get preparing() {
        return this.asPreparingTaskGroup({
            prepareCommands() {
                this.application.commands = [];
                this.application.nodeVersion = NODE_VERSION;
                if (!this.application[GENERATORS])
                    return;
                for (const generator of Object.keys(this.application[GENERATORS])) {
                    const subGeneratorConfig = this.getSubGeneratorStorage(generator).getAll();
                    if (subGeneratorConfig.command) {
                        this.application.commands.push(generator);
                    }
                }
            },
            preparePath() {
                this.application.blueprintsPath = this.application[LOCAL_BLUEPRINT_OPTION] ? '.blueprint/' : 'generators/';
            },
            prepare({ application }) {
                const { cli, cliName, baseName } = this.application;
                this.application.githubRepository = this.jhipsterConfig.githubRepository ?? `jhipster/generator-jhipster-${baseName}`;
                application.blueprintMjsExtension = this.application.js ? 'js' : 'mjs';
                if (cli) {
                    this.application.cliName = cliName ?? `jhipster-${baseName}`;
                }
            },
        });
    }
    get [BaseGenerator.PREPARING]() {
        return this.delegateTasksToBlueprint(() => this.preparing);
    }
    get writing() {
        return this.asWritingTaskGroup({
            async cleanup({ control }) {
                await control.cleanupFiles({
                    '8.5.1': ['.eslintrc.json'],
                    '8.7.2': ['.eslintignore', 'vitest.test-setup.ts'],
                    '8.7.4': ['.blueprint/generate-sample/get-samples.mjs', '.blueprint/github-build-matrix/build-matrix.mjs'],
                });
            },
            async writing({ application }) {
                this.application.sampleWritten = this.jhipsterConfig.sampleWritten;
                const { skipWorkflows, ignoreExistingGenerators } = this;
                await this.writeFiles({
                    sections: files,
                    context: {
                        ...application,
                        skipWorkflows,
                        ignoreExistingGenerators,
                        ...this.application,
                    },
                });
                this.jhipsterConfig.sampleWritten = true;
            },
            async writingGenerators({ application }) {
                if (!this.application[GENERATORS])
                    return;
                const { skipWorkflows, ignoreExistingGenerators } = this;
                for (const generator of Object.keys(this.application[GENERATORS])) {
                    const subGeneratorStorage = this.getSubGeneratorStorage(generator);
                    const subGeneratorConfig = subGeneratorStorage.getAll();
                    const priorities = (subGeneratorConfig[PRIORITIES] || []).map(priority => ({
                        name: priority,
                        asTaskGroup: `as${upperFirst(priority)}TaskGroup`,
                        constant: `${snakeCase(priority).toUpperCase()}`,
                    }));
                    const customGenerator = !Object.values(GENERATOR_LIST).includes(generator);
                    const jhipsterGenerator = customGenerator || subGeneratorConfig.sbs ? 'base-application' : generator;
                    const subTemplateData = {
                        ...application,
                        skipWorkflows,
                        ignoreExistingGenerators,
                        application: this.application,
                        ...defaultSubGeneratorConfig(),
                        ...subGeneratorConfig,
                        generator,
                        customGenerator,
                        jhipsterGenerator,
                        subGenerator: generator,
                        generatorClass: upperFirst(camelCase(jhipsterGenerator)),
                        priorities,
                    };
                    await this.writeFiles({
                        sections: generatorFiles,
                        context: subTemplateData,
                    });
                    subGeneratorStorage.set(WRITTEN, true);
                }
            },
        });
    }
    get [BaseGenerator.WRITING]() {
        return this.delegateTasksToBlueprint(() => this.writing);
    }
    get postWriting() {
        return this.asPostWritingTaskGroup({
            upgrade({ application }) {
                if (!this.application[GENERATORS])
                    return;
                if (!this.isJhipsterVersionLessThan('8.7.2'))
                    return;
                for (const generator of Object.keys(this.application[GENERATORS])) {
                    const commandFile = `${this.application.blueprintsPath}${generator}/command.${application.blueprintMjsExtension}`;
                    this.editFile(commandFile, { ignoreNonExisting: true }, content => content
                        .replace(`/**
 * @type {import('generator-jhipster').JHipsterCommandDefinition}
 */`, `import { asCommand } from 'generator-jhipster';
`)
                        .replace('const command = ', 'export default asCommand(')
                        .replace(`
};`, '});')
                        .replace('export default command;', ''));
                    const generatorSpec = `${this.application.blueprintsPath}${generator}/generator.spec.${application.blueprintMjsExtension}`;
                    this.editFile(generatorSpec, { ignoreNonExisting: true }, content => content.replaceAll(/blueprint: '([\w-]*)'/g, "blueprint: ['$1']"));
                }
            },
            packageJson() {
                if (this.jhipsterConfig[LOCAL_BLUEPRINT_OPTION])
                    return;
                const { packagejs } = this.application;
                const mainDependencies = {
                    ...packagejs.dependencies,
                    ...packagejs.devDependencies,
                };
                this.loadNodeDependenciesFromPackageJson(mainDependencies, this.fetchFromInstalledJHipster('generate-blueprint/resources/package.json'));
                this.packageJson.merge({
                    name: `generator-jhipster-${this.jhipsterConfig.baseName}`,
                    keywords: ['yeoman-generator', 'jhipster-blueprint', BLUEPRINT_API_VERSION],
                    files: defaultPublishedFiles,
                    scripts: {
                        ejslint: 'ejslint generators/**/*.ejs',
                        lint: 'eslint .',
                        'lint-fix': 'npm run ejslint && npm run lint -- --fix',
                        pretest: 'npm run prettier-check && npm run lint',
                        test: 'vitest run',
                        'update-snapshot': 'vitest run --update',
                        vitest: 'vitest',
                    },
                    devDependencies: {
                        'ejs-lint': `${mainDependencies['ejs-lint']}`,
                        eslint: `${mainDependencies.eslint}`,
                        globals: `${mainDependencies.globals}`,
                        vitest: mainDependencies.vitest,
                        prettier: `${mainDependencies.prettier}`,
                        'yeoman-test': '>=8.2.0',
                    },
                    engines: {
                        node: packagejs.engines.node,
                    },
                });
            },
            addCliToPackageJson() {
                if (!this.jhipsterConfig.cli || this.jhipsterConfig[LOCAL_BLUEPRINT_OPTION])
                    return;
                const { cliName } = this.application;
                this.packageJson.merge({
                    bin: {
                        [cliName]: 'cli/cli.cjs',
                    },
                    files: ['cli', ...defaultPublishedFiles],
                });
            },
            addGeneratorJHipsterDependency() {
                if (this.jhipsterConfig[LOCAL_BLUEPRINT_OPTION])
                    return;
                const { packagejs } = this.application;
                const exactDependency = {
                    'generator-jhipster': `${packagejs.version}`,
                };
                const caretDependency = {
                    'generator-jhipster': `^${packagejs.version}`,
                };
                if (this.jhipsterConfig.dynamic) {
                    this.packageJson.merge({
                        devDependencies: exactDependency,
                        peerDependencies: caretDependency,
                        engines: caretDependency,
                    });
                }
                else {
                    this.packageJson.merge({
                        dependencies: this.gitDependency ? { 'generator-jhipster': this.gitDependency } : exactDependency,
                        engines: this.jhipsterConfig.caret ? caretDependency : exactDependency,
                    });
                }
            },
        });
    }
    get [BaseGenerator.POST_WRITING]() {
        return this.delegateTasksToBlueprint(() => this.postWriting);
    }
    get postInstall() {
        return this.asPostInstallTaskGroup({
            async addSnapshot({ control }) {
                const { [LOCAL_BLUEPRINT_OPTION]: localBlueprint } = this.jhipsterConfig;
                const { skipInstall, skipGit, existed, [GENERATE_SNAPSHOTS]: generateSnapshots = !localBlueprint && !skipInstall && !skipGit && !existed, } = this.options;
                if (!generateSnapshots)
                    return;
                if (this.recreatePackageLock) {
                    await rm(this.destinationPath('package-lock.json'), { force: true });
                    await rm(this.destinationPath('node_modules'), { force: true, recursive: true });
                    await this.spawnCommand('npm', ['install'], { stdio: 'inherit' });
                }
                try {
                    if (this.options[LINK_JHIPSTER_DEPENDENCY]) {
                        this.log.verboseInfo('Linking generator-begcode');
                        await this.spawnCommand('npm', ['link', 'generator-begcode'], { stdio: 'inherit' });
                    }
                    this.log.verboseInfo(`
This is a new blueprint, executing '${chalk.yellow('npm run update-snapshot')}' to generate snapshots and commit to git.`);
                    await this.spawnCommand('npm', ['run', 'update-snapshot']);
                }
                catch (error) {
                    if (generateSnapshots !== undefined) {
                        throw error;
                    }
                    this.log.warn('Fail to generate snapshots');
                }
                if (control.jhipsterOldVersion) {
                    try {
                        await this.spawnCommand('npm', ['run', 'prettier-format']);
                    }
                    catch {
                    }
                    try {
                        await this.spawnCommand('npm', ['run', 'lint-fix']);
                    }
                    catch {
                    }
                }
            },
        });
    }
    get [BaseGenerator.POST_INSTALL]() {
        return this.delegateTasksToBlueprint(() => this.postInstall);
    }
    get end() {
        return this.asEndTaskGroup({
            end() {
                if (this.jhipsterConfig[LOCAL_BLUEPRINT_OPTION])
                    return;
                this.log.log(`${chalk.bold.green('##### USAGE #####')}
To begin to work:
- launch: ${chalk.yellow.bold('npm install')}
- link: ${chalk.yellow.bold('npm link')}
- link begcode: ${chalk.yellow.bold('npm link generator-begcode')}
- test your module in a JHipster project:
    - create a new directory and go into it
    - link the blueprint: ${chalk.yellow.bold(`npm link generator-begcode-${this.moduleName}`)}
    - launch JHipster with flags: ${chalk.yellow.bold(`begcode --blueprints ${this.moduleName}`)}
- then, come back here, and begin to code!
`);
            },
        });
    }
    get [BaseGenerator.END]() {
        return this.delegateTasksToBlueprint(() => this.end);
    }
    getSubGeneratorStorage(subGenerator) {
        return this.config.createStorage(`${GENERATORS}.${subGenerator}`);
    }
    validateGitHubName(input) {
        if (/^([a-zA-Z0-9]+)(-([a-zA-Z0-9])+)*$/.test(input) && input !== '')
            return true;
        return 'Your username is mandatory, cannot contain special characters or a blank space';
    }
    validateModuleName(input) {
        return /^[a-zA-Z0-9-]+$/.test(input)
            ? true
            : 'Your blueprint name is mandatory, cannot contain special characters or a blank space, using the default name instead';
    }
}
