import { EventEmitter } from 'node:events';
import { mock } from 'node:test';
import { type Store } from 'mem-fs';
import type { BaseEnvironmentOptions, BaseGenerator, GetGeneratorConstructor, GetGeneratorOptions, LookupOptions, PromptAnswers } from '@yeoman/types';
import { type MemFsEditor, type MemFsEditorFile } from 'mem-fs-editor';
import type { DefaultEnvironmentApi, DefaultGeneratorApi } from '../types/type-helpers.js';
import RunResult, { type RunResultOptions } from './run-result.js';
import { type CreateEnv as CreateEnvironment, type Dependency, type YeomanTest } from './helpers.js';
import { type AskedQuestions, type DummyPromptOptions, type TestAdapterOptions } from './adapter.js';
/**
 * Provides settings for creating a `RunContext`.
 */
export type RunContextSettings = {
    /**
     * Automatically run this generator in a tmp dir
     * @default true
     */
    tmpdir?: boolean;
    cwd?: string;
    oldCwd?: string;
    forwardCwd?: boolean;
    autoCleanup?: boolean;
    memFs?: Store<MemFsEditorFile>;
    /**
     * File path to the generator (only used if Generator is a constructor)
     */
    resolved?: string;
    /**
     * Namespace (only used if Generator is a constructor)
     * @default 'gen:test'
     */
    namespace?: string;
};
type PromiseRunResult<GeneratorType extends BaseGenerator> = Promise<RunResult<GeneratorType>>;
type MockedGeneratorFactory<GenParameter extends BaseGenerator = DefaultGeneratorApi> = (GeneratorClass?: GetGeneratorConstructor<GenParameter>) => GetGeneratorConstructor<GenParameter>;
type EnvironmentOptions = BaseEnvironmentOptions & {
    createEnv?: CreateEnvironment;
};
export declare class RunContextBase<GeneratorType extends BaseGenerator = DefaultGeneratorApi> extends EventEmitter {
    readonly mockedGenerators: Record<string, BaseGenerator>;
    env: DefaultEnvironmentApi;
    generator: GeneratorType;
    readonly settings: RunContextSettings;
    readonly envOptions: EnvironmentOptions;
    completed: boolean;
    targetDirectory?: string;
    editor: MemFsEditor;
    memFs: Store<MemFsEditorFile>;
    spawnStub?: any;
    mockedGeneratorFactory: MockedGeneratorFactory;
    readonly askedQuestions: AskedQuestions;
    protected environmentPromise?: PromiseRunResult<GeneratorType>;
    private args;
    private options;
    private answers?;
    private readonly adapterOptions;
    private keepFsState?;
    private readonly onGeneratorCallbacks;
    private readonly onTargetDirectoryCallbacks;
    private readonly onEnvironmentCallbacks;
    private readonly inDirCallbacks;
    protected readonly Generator?: string | GetGeneratorConstructor<GeneratorType>;
    protected readonly helpers: YeomanTest;
    private readonly temporaryDir;
    private oldCwd?;
    private eventListenersSet;
    private envCB;
    private built;
    private ran;
    private errored;
    private readonly beforePrepareCallbacks;
    private environmentRun?;
    /**
     * This class provide a run context object to façade the complexity involved in setting
     * up a generator for testing
     * @constructor
     * @param Generator - Namespace or generator constructor. If the later
     *                                       is provided, then namespace is assumed to be
     *                                       'gen:test' in all cases
     * @param settings
     * @return {this}
     */
    constructor(generatorType?: string | GetGeneratorConstructor<GeneratorType>, settings?: RunContextSettings, environmentOptions?: EnvironmentOptions, helpers?: YeomanTest);
    /**
     * Run the generator on the environment and promises a RunResult instance.
     * @return {PromiseRunResult} Promise a RunResult instance.
     */
    run(): PromiseRunResult<GeneratorType>;
    on(eventName: string | symbol, listener: (...arguments_: any[]) => void): this;
    /**
     * @deprecated
     * Clean the provided directory, then change directory into it
     * @param  dirPath - Directory path (relative to CWD). Prefer passing an absolute
     *                            file path for predictable results
     * @param [cb] - callback who'll receive the folder path as argument
     * @return run context instance
     */
    inDir(dirPath: string, callback?: (folderPath: string) => void): this;
    /**
     * Register an callback to prepare the destination folder.
     * @param [cb]  - callback who'll receive the folder path as argument
     * @return this - run context instance
     */
    doInDir(callback: (folderPath: string) => void): this;
    /**
     * @deprecated
     * Change directory without deleting directory content.
     * @param  dirPath - Directory path (relative to CWD). Prefer passing an absolute
     *                            file path for predictable results
     * @return run context instance
     */
    cd(dirPath: string): this;
    /**
     * Cleanup a temporary directory and change the CWD into it
     *
     * This method is called automatically when creating a RunContext. Only use it if you need
     * to use the callback.
     *
     * @param [cb]  - callback who'll receive the folder path as argument
     * @return this - run context instance
     */
    inTmpDir(callback?: (folderPath: string) => void): this;
    /**
     * Restore cwd to initial cwd.
     * @return {this} run context instance
     */
    restore(): this;
    /**
     * Clean the directory used for tests inside inDir/inTmpDir
     * @param  {Boolean} force - force directory cleanup for not tmpdir
     */
    cleanup(): void;
    /**
     * Clean the directory used for tests inside inDir/inTmpDir
     * @param  {Boolean} force - force directory cleanup for not tmpdir
     */
    cleanupTemporaryDir(): void;
    /**
     * Clean the directory used for tests inside inDir/inTmpDir
     * @param force - force directory cleanup for not tmpdir
     */
    cleanTestDirectory(force?: boolean): void;
    /**
     * TestAdapter options.
     */
    withAdapterOptions(options: Omit<TestAdapterOptions, 'mockedAnswers'>): this;
    /**
     * Create an environment
     *
     * This method is called automatically when creating a RunContext. Only use it if you need
     * to use the callback.
     *
     * @param {Function} [cb] - callback who'll receive the folder path as argument
     * @return {this} run context instance
     */
    withEnvironment(callback: (this: this, environment: DefaultEnvironmentApi) => DefaultEnvironmentApi | void): this;
    /**
     * Customize enviroment run method.
     *
     * @param callback
     * @return {this} run context instance
     */
    withEnvironmentRun(callback: (this: this, env: DefaultEnvironmentApi, gen: GeneratorType) => void): this;
    /**
     * Run lookup on the environment.
     *
     * @param lookups - lookup to run.
     */
    withLookups(lookups: LookupOptions | LookupOptions[]): this;
    /**
     * Provide arguments to the run context
     * @param  args - command line arguments as Array or space separated string
     */
    withArguments(arguments_: string | string[]): this;
    /**
     * Provide options to the run context
     * @param  {Object} options - command line options (e.g. `--opt-one=foo`)
     * @return {this}
     */
    withOptions(options: Partial<Omit<GetGeneratorOptions<GeneratorType>, 'env' | 'namespace' | 'resolved'>>): this;
    /**
     * @deprecated
     * Mock the prompt with dummy answers
     * @param  answers - Answers to the prompt questions
     * @param  options - Options or callback.
     * @param  {Function} [options.callback] - Callback.
     * @param  {Boolean} [options.throwOnMissingAnswer] - Throw if a answer is missing.
     * @return {this}
     */
    withPrompts(answers: PromptAnswers, options?: Omit<DummyPromptOptions, 'mockedAnswers'>): this;
    /**
     * Mock answers for prompts
     * @param  answers - Answers to the prompt questions
     * @param  options - Options or callback.
     * @return {this}
     */
    withAnswers(answers: PromptAnswers, options?: Omit<DummyPromptOptions, 'mockedAnswers'>): this;
    /**
     * Provide dependent generators
     * @param {Array} dependencies - paths to the generators dependencies
     * @return {this}
     * @example
     * var angular = new RunContext('../../app');
     * angular.withGenerators([
     *   '../../common',
     *   '../../controller',
     *   '../../main',
     *   [helpers.createDummyGenerator(), 'testacular:app']
     * ]);
     * angular.on('end', function () {
     *   // assert something
     * });
     */
    withGenerators(dependencies: Dependency[]): this;
    withSpawnMock<StubType = ReturnType<typeof mock.fn>>(options?: ((...arguments_: any[]) => any) | {
        stub?: (...arguments_: any[]) => any;
        registerNodeMockDefaults?: boolean;
        callback?: ({ stub, implementation }: {
            stub: StubType;
            implementation: any;
        }) => void | Promise<void>;
    }): this;
    withMockedGeneratorFactory(mockedGeneratorFactory: MockedGeneratorFactory): this;
    /**
     * Create mocked generators
     * @param namespaces - namespaces of mocked generators
     * @return this
     * @example
     * var angular = helpers
     *   .create('../../app')
     *   .withMockedGenerators([
     *     'foo:app',
     *     'foo:bar',
     *   ])
     *   .run()
     *   .then(runResult => assert(runResult
     *     .mockedGenerators['foo:app']
   .calledOnce));
     */
    withMockedGenerators(namespaces: string[]): this;
    /**
     * Mock the local configuration with the provided config
     * @param localConfig - should look just like if called config.getAll()
     */
    withLocalConfig(localConfig: any): this;
    /**
     * Don't reset mem-fs state cleared to aggregate snapshots from multiple runs.
     */
    withKeepFsState(): this;
    /**
     * Add files to mem-fs.
     * Files will be resolved relative to targetDir.
     *
     * Files with Object content will be merged to existing content.
     * To avoid merging, `JSON.stringify` the content.
     */
    withFiles(files: Record<string, string | Record<string, unknown>>): this;
    withFiles(relativePath: string, files: Record<string, string | Record<string, unknown>>): this;
    /**
     * Add .yo-rc.json to mem-fs.
     *
     * @param content
     * @returns
     */
    withYoRc(content: string | Record<string, unknown>): this;
    /**
     * Add a generator config to .yo-rc.json
     */
    withYoRcConfig(key: string, content: Record<string, unknown>): this;
    /**
     * Commit mem-fs files.
     */
    commitFiles(): this;
    /**
     * Execute callback after targetDirectory is set
     * @param callback
     * @returns
     */
    onTargetDirectory(callback: (this: this, targetDirectory: string) => any): this;
    /**
     * Execute callback after generator is ready
     * @param callback
     * @returns
     */
    onGenerator(callback: (this: this, generator: GeneratorType) => any): this;
    /**
     * Execute callback prefore parepare
     * @param callback
     * @returns
     */
    onBeforePrepare(callback: (this: this) => void | Promise<void>): this;
    /**
     * Execute callback after environment is ready
     * @param callback
     * @returns
     */
    onEnvironment(callback: (this: this, environment: DefaultEnvironmentApi) => any): this;
    prepare(): Promise<void>;
    protected assertNotBuild(): void;
    /**
     * Build the generator and the environment.
     * @return {RunContext|false} this
     */
    build(): Promise<void>;
    /**
     * Return a promise representing the generator run process
     * @return Promise resolved on end or rejected on error
     */
    protected toPromise(): PromiseRunResult<GeneratorType>;
    protected _createRunResultOptions(): RunResultOptions<GeneratorType>;
    /**
     * Keeps compatibility with events
     */
    private setupEventListeners;
    /**
     * Set the target directory.
     * @private
     * @param  {String} dirPath - Directory path (relative to CWD). Prefer passing an absolute
     *                            file path for predictable results
     * @return {this} run context instance
     */
    private setDir;
}
export default class RunContext<GeneratorType extends BaseGenerator = BaseGenerator> extends RunContextBase<GeneratorType> implements Promise<RunResult<GeneratorType>> {
    then<TResult1 = RunResult<GeneratorType>, TResult2 = never>(onfulfilled?: ((value: RunResult<GeneratorType>) => TResult1 | PromiseLike<TResult1>) | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined): Promise<TResult1 | TResult2>;
    catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined): Promise<RunResult<GeneratorType> | TResult>;
    finally(onfinally?: (() => void) | undefined): Promise<RunResult<GeneratorType>>;
    get [Symbol.toStringTag](): string;
}
export declare class BasicRunContext<GeneratorType extends BaseGenerator = BaseGenerator> extends RunContext<GeneratorType> {
    run(): PromiseRunResult<any>;
}
export {};
