import { Duplex } from 'stream';
import { forceYoFiles, createConflicterTransform, createYoResolveTransform } from '@yeoman/conflicter';
import { isFileStateModified, isFilePending } from 'mem-fs-editor/state';
import { createCommitTransform } from 'mem-fs-editor/transform';
import BaseGenerator from '../base/index.mjs';
import { createMultiStepTransform, createPrettierTransform, createForceWriteConfigFilesTransform, autoCrlfTransform, isPrettierConfigFilePath, createSortConfigFilesTransform, createESLintTransform, createRemoveUnusedImportsTransform, } from './support/index.mjs';
import { PRETTIER_EXTENSIONS } from '../generator-constants.mjs';
import { GENERATOR_BOOTSTRAP, GENERATOR_UPGRADE } from '../generator-list.mjs';
import { PRIORITY_NAMES, QUEUES } from '../base-application/priorities.mjs';
import command from './command.mjs';
import { loadStoredAppOptions } from '../app/support/index.mjs';
const { MULTISTEP_TRANSFORM, PRE_CONFLICTS } = PRIORITY_NAMES;
const { MULTISTEP_TRANSFORM_QUEUE, PRE_CONFLICTS_QUEUE } = QUEUES;
const MULTISTEP_TRANSFORM_PRIORITY = BaseGenerator.asPriority(MULTISTEP_TRANSFORM);
const PRE_CONFLICTS_PRIORITY = BaseGenerator.asPriority(PRE_CONFLICTS);
export default class BootstrapGenerator extends BaseGenerator {
    static MULTISTEP_TRANSFORM = MULTISTEP_TRANSFORM_PRIORITY;
    static PRE_CONFLICTS = PRE_CONFLICTS_PRIORITY;
    upgradeCommand;
    skipPrettier;
    prettierExtensions = PRETTIER_EXTENSIONS.split(',');
    prettierOptions = { plugins: [] };
    refreshOnCommit = false;
    constructor(args, options, features) {
        super(args, options, { jhipsterBootstrap: false, uniqueGlobally: true, customCommitTask: () => this.commitTask(), ...features });
    }
    async beforeQueue() {
        loadStoredAppOptions.call(this);
        this.env.options.nodePackageManager = 'npm';
        this.upgradeCommand = this.options.commandName === GENERATOR_UPGRADE;
        if (!this.fromBlueprint) {
            await this.composeWithBlueprints(GENERATOR_BOOTSTRAP);
        }
        if (this.delegateToBlueprint) {
            throw new Error('Only sbs blueprint is supported');
        }
    }
    get initializing() {
        return this.asInitializingTaskGroup({
            loadOptions() {
                this.parseJHipsterOptions(command.options, command.configs);
            },
            validateBlueprint() {
                if (this.jhipsterConfig.blueprints && !this.skipChecks) {
                    this.jhipsterConfig.blueprints.forEach(blueprint => {
                        this._checkJHipsterBlueprintVersion(blueprint.name);
                        this._checkBlueprint(blueprint.name);
                    });
                }
            },
        });
    }
    get [BaseGenerator.INITIALIZING]() {
        return this.delegateTasksToBlueprint(() => this.initializing);
    }
    get multistepTransform() {
        return {
            queueMultistepTransform() {
                this.queueMultistepTransform();
            },
        };
    }
    get [MULTISTEP_TRANSFORM_PRIORITY]() {
        return this.multistepTransform;
    }
    get preConflicts() {
        return {
            queueCommitPrettierConfig() {
                this.queueCommitPrettierConfig();
            },
        };
    }
    get [PRE_CONFLICTS_PRIORITY]() {
        return this.preConflicts;
    }
    queueMultistepTransform() {
        const multiStepTransform = createMultiStepTransform();
        const listener = filePath => {
            if (multiStepTransform.templateFileFs.isTemplate(filePath)) {
                this.env.sharedFs.removeListener('change', listener);
                this.queueMultistepTransform();
            }
        };
        this.queueTask({
            method: async () => {
                await this.pipeline({
                    name: 'applying multi-step templates',
                    filter: file => isFileStateModified(file) && multiStepTransform.templateFileFs.isTemplate(file.path),
                    refresh: true,
                    allowOverride: true,
                }, multiStepTransform);
                this.env.sharedFs.on('change', listener);
            },
            taskName: MULTISTEP_TRANSFORM_QUEUE,
            queueName: MULTISTEP_TRANSFORM_QUEUE,
            once: true,
        });
    }
    queueCommitPrettierConfig() {
        const listener = filePath => {
            if (isPrettierConfigFilePath(filePath)) {
                this.env.sharedFs.removeListener('change', listener);
                this.queueCommitPrettierConfig();
            }
        };
        this.queueTask({
            method: async () => {
                await this.commitPrettierConfig();
                this.env.sharedFs.on('change', listener);
            },
            taskName: 'commitPrettierConfig',
            queueName: PRE_CONFLICTS_QUEUE,
            once: true,
        });
    }
    async commitPrettierConfig() {
        await this.commitSharedFs({
            log: 'prettier configuration files committed to disk',
            filter: file => isPrettierConfigFilePath(file.path),
        });
    }
    async commitTask() {
        await this.commitSharedFs({ refresh: this.refreshOnCommit }, ...this.env
            .findFeature('commitTransformFactory')
            .map(({ feature }) => feature())
            .flat());
    }
    async commitSharedFs({ log, ...options } = {}, ...transforms) {
        const skipYoResolveTransforms = [];
        if (!this.options.skipYoResolve) {
            skipYoResolveTransforms.push(createYoResolveTransform());
        }
        const prettierTransforms = [];
        if (!this.skipPrettier) {
            const ignoreErrors = this.options.ignoreErrors || this.upgradeCommand;
            prettierTransforms.push(createESLintTransform.call(this, { ignoreErrors, extensions: 'ts,js' }), createRemoveUnusedImportsTransform.call(this, { ignoreErrors }), await createPrettierTransform.call(this, {
                ignoreErrors,
                prettierPackageJson: true,
                prettierJava: !this.jhipsterConfig.skipServer,
                extensions: this.prettierExtensions.join(','),
                prettierOptions: this.prettierOptions,
            }));
        }
        const autoCrlfTransforms = [];
        if (this.jhipsterConfig.autoCrlf) {
            autoCrlfTransforms.push(await autoCrlfTransform({ baseDir: this.destinationPath() }));
        }
        const transformStreams = [
            ...skipYoResolveTransforms,
            forceYoFiles(),
            createSortConfigFilesTransform(),
            createForceWriteConfigFilesTransform(),
            ...prettierTransforms,
            ...autoCrlfTransforms,
            createConflicterTransform(this.env.adapter, { ...this.env.conflicterOptions }),
            createCommitTransform(),
        ];
        await this.pipeline({
            refresh: false,
            pendingFiles: false,
            ...options,
            disabled: true,
        }, ...transforms, Duplex.from(async function* (files) {
            for await (const file of files) {
                if (isFilePending(file)) {
                    yield file;
                }
            }
        }), ...transformStreams);
        this.log.ok(log ?? 'files committed to disk');
    }
}
