/**
 * 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 { rm } from 'node:fs/promises';
import { createConflicterTransform, createYoResolveTransform, forceYoFiles } from '@yeoman/conflicter';
import { transform } from '@yeoman/transform';
import { isFilePending, isFileStateModified } from 'mem-fs-editor/state';
import { createCommitTransform } from 'mem-fs-editor/transform';
import BaseGenerator, { CommandBaseGenerator } from "../base/index.js";
import { PRIORITY_NAMES, QUEUES } from "../base-application/priorities.js";
import { createNeedleTransform } from "../base-core/support/needles.js";
import { PRETTIER_EXTENSIONS } from "../generator-constants.js";
import { autoCrlfTransform, createESLintTransform, createForceWriteConfigFilesTransform, createMultiStepTransform, createPrettierTransform, createSortConfigFilesTransform, isPrettierConfigFilePath, } from "./support/index.js";
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 CommandBaseGenerator {
    static MULTISTEP_TRANSFORM = MULTISTEP_TRANSFORM_PRIORITY;
    static PRE_CONFLICTS = PRE_CONFLICTS_PRIORITY;
    upgradeCommand;
    skipPrettier;
    skipEslint;
    prettierExtensions = PRETTIER_EXTENSIONS.split(',');
    prettierOptions = { plugins: [] };
    refreshOnCommit = false;
    constructor(args, options, features) {
        super(args, options, { uniqueGlobally: true, customCommitTask: () => this.commitTask(), ...features });
    }
    async beforeQueue() {
        // Force npm override later if needed
        this.env.options.nodePackageManager = 'npm';
        this.upgradeCommand = this.options.commandName === 'upgrade';
        if (!this.fromBlueprint) {
            await this.composeWithBlueprints();
        }
        if (this.delegateToBlueprint) {
            throw new Error('Only sbs blueprint is supported');
        }
    }
    get multistepTransform() {
        return {
            queueMultistepTransform() {
                this.queueMultistepTransform();
            },
        };
    }
    get [MULTISTEP_TRANSFORM_PRIORITY]() {
        return this.multistepTransform;
    }
    get preConflicts() {
        return this.asAnyTaskGroup({
            queueCommitPrettierConfig() {
                this.queueCommitPrettierConfig();
            },
        });
    }
    get [PRE_CONFLICTS_PRIORITY]() {
        return this.preConflicts;
    }
    /**
     * Queue multi step templates transform
     */
    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,
                    resolveConflict: (current, newFile) => (isFileStateModified(current) ? current : newFile),
                }, 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').flatMap(({ feature }) => feature()));
    }
    /**
     * Commits the MemFs to the disc.
     */
    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;
            if (!this.skipEslint) {
                prettierTransforms.push(await createESLintTransform.call(this, { ignoreErrors }));
            }
            prettierTransforms.push(await createPrettierTransform.call(this, {
                ignoreErrors,
                prettierPackageJson: true,
                prettierJava: !this.jhipsterConfig.skipServer,
                extensions: this.prettierExtensions.join(','),
                prettierOptions: this.prettierOptions,
            }));
        }
        const autoCrlfTransforms = [];
        if (this.jhipsterConfigWithDefaults.autoCrlf) {
            autoCrlfTransforms.push(await autoCrlfTransform({ baseDir: this.destinationPath() }));
        }
        let customizeActions;
        if (this.options.devBlueprintEnabled) {
            customizeActions = (actions, { separator }) => {
                return [
                    ...actions,
                    ...(separator ? [separator()] : []),
                    {
                        key: 't',
                        name: 'apply to template',
                        value: async ({ file }) => {
                            const { applyChangesToFileOrCopy } = await import("../../lib/ci/apply-patch-to-template.js");
                            if (file.history?.[0] && file.conflicterData?.diskContents) {
                                const templateFile = file.history[0];
                                if (file.contents) {
                                    const oldFileContents = file.conflicterData.diskContents.toString();
                                    const newFileContents = file.contents.toString();
                                    applyChangesToFileOrCopy({ templateFile, oldFileContents, newFileContents });
                                }
                                else {
                                    await rm(templateFile, { force: true });
                                }
                            }
                            return 'skip';
                        },
                    },
                ];
            };
        }
        const removeNeedlesTransforms = [];
        if (this.jhipsterConfig.removeNeedles) {
            removeNeedlesTransforms.push(createNeedleTransform());
        }
        const transformStreams = [
            ...skipYoResolveTransforms,
            forceYoFiles(),
            createSortConfigFilesTransform(),
            createForceWriteConfigFilesTransform(),
            ...removeNeedlesTransforms,
            ...prettierTransforms,
            ...autoCrlfTransforms,
            createConflicterTransform(this.env.adapter, { ...this.env.conflicterOptions, customizeActions }),
            createCommitTransform(),
        ];
        await this.pipeline({
            refresh: false,
            // Let pending files pass through.
            pendingFiles: false,
            ...options,
            // Disable progress since it blocks stdin.
            disabled: true,
        }, ...transforms, 
        // Filter out pending files.
        transform((file) => (isFilePending(file) ? file : undefined)), ...transformStreams);
        this.log.ok(log ?? 'files committed to disk');
    }
}
