import { Linter } from 'eslint';

/**
 * A function that returns the config as-is, useful for providing type hints.
 */
declare function defineFlatConfig<T extends Linter.FlatConfig = Linter.FlatConfig>(config: T): T;

/**
 * Merge multiple flat configs into a single flat config.
 *
 * Note there is no guarantee that the result works the same as the original configs.
 */
declare function mergeConfigs<T extends Linter.FlatConfig = Linter.FlatConfig>(...configs: T[]): T;

/**
 * Alias to `Linter.FlatConfig`
 *
 * @deprecated
 */
interface FlatConfigItem extends Linter.FlatConfig {
}
/**
 * A type that can be awaited. Promise<T> or T.
 */
type Awaitable<T> = T | Promise<T>;
/**
 * A type that can be an array or a single item.
 */
type Arrayable<T> = T | T[];
/**
 * Default config names map. Used for type augmentation.
 *
 * @example
 * ```ts
 * declare module 'eslint-flat-config-utils' {
 *   interface DefaultConfigNamesMap {
 *     'my-custom-config': true
 *   }
 * }
 * ```
 */
interface DefaultConfigNamesMap {
}
interface Nothing {
}
/**
 * type StringLiteralUnion<'foo'> = 'foo' | string
 * This has auto completion whereas `'foo' | string` doesn't
 * Adapted from https://github.com/microsoft/TypeScript/issues/29729
 */
type StringLiteralUnion<T extends U, U = string> = T | (U & Nothing);
type FilterType<T, F> = T extends F ? T : never;
type NullableObject<T> = {
    [K in keyof T]?: T[K] | null | undefined;
};
type GetRuleRecordFromConfig<T> = T extends {
    rules?: infer R;
} ? R : Linter.RulesRecord;

/**
 * Concat multiple flat configs into a single flat config array.
 *
 * It also resolves promises and flattens the result.
 *
 * @example
 *
 * ```ts
 * import { concat } from 'eslint-flat-config-utils'
 * import eslint from '@eslint/js'
 * import stylistic from '@stylistic/eslint-plugin'
 *
 * export default concat(
 *   eslint,
 *   stylistic.configs.customize(),
 *   { rules: { 'no-console': 'off' } },
 *   // ...
 * )
 * ```
 */
declare function concat<T extends Linter.FlatConfig = Linter.FlatConfig>(...configs: Awaitable<T | T[]>[]): Promise<T[]>;

/**
 * Rename plugin prefixes in a rule object.
 * Accepts a map of prefixes to rename.
 *
 * @example
 * ```ts
 * import { renamePluginsInRules } from 'eslint-flat-config-utils'
 *
 * export default [{
 *   rules: renamePluginsInRules(
 *     {
 *       '@typescript-eslint/indent': 'error'
 *     },
 *     { '@typescript-eslint': 'ts' }
 *   )
 * }]
 * ```
 */
declare function renamePluginsInRules(rules: Record<string, any>, map: Record<string, string>): Record<string, any>;
/**
 * Rename plugin names a flat configs array
 *
 * @example
 * ```ts
 * import { renamePluginsInConfigs } from 'eslint-flat-config-utils'
 * import someConfigs from './some-configs'
 *
 * export default renamePluginsInConfigs(someConfigs, {
 *   '@typescript-eslint': 'ts',
 *   'import-x': 'import',
 * })
 * ```
 */
declare function renamePluginsInConfigs<T extends Linter.FlatConfig = Linter.FlatConfig>(configs: T[], map: Record<string, string>): T[];

/**
 * Awaitable array of ESLint flat configs or a composer object.
 */
type ResolvableFlatConfig<T extends Linter.FlatConfig = Linter.FlatConfig> = Awaitable<Arrayable<T>> | Awaitable<Linter.FlatConfig[]> | FlatConfigComposer<any>;
/**
 * Create a chainable composer object that makes manipulating ESLint flat config easier.
 *
 * It extends Promise, so that you can directly await or export it to `eslint.config.mjs`
 *
 * ```ts
 * // eslint.config.mjs
 * import { composer } from 'eslint-flat-config-utils'
 *
 * export default composer(
 *   {
 *     plugins: {},
 *     rules: {},
 *   }
 *   // ...some configs, accepts same arguments as `concat`
 * )
 *   .append(
 *     // appends more configs at the end, accepts same arguments as `concat`
 *   )
 *   .prepend(
 *     // prepends more configs at the beginning, accepts same arguments as `concat`
 *   )
 *   .insertAfter(
 *     'config-name', // specify the name of the target config, or index
 *     // insert more configs after the target, accepts same arguments as `concat`
 *   )
 *   .renamePlugins({
 *     // rename plugins
 *     'old-name': 'new-name',
 *     // for example, rename `n` from `eslint-plugin-n` to more a explicit prefix `node`
 *     'n': 'node'
 *     // applies to all plugins and rules in the configs
 *   })
 *   .override(
 *     'config-name', // specify the name of the target config, or index
 *     {
 *       // merge with the target config
 *       rules: {
 *         'no-console': 'off'
 *       },
 *     }
 *   )
 *
 * // And you an directly return the composer object to `eslint.config.mjs`
 * ```
 */
declare function composer<T extends Linter.FlatConfig = Linter.FlatConfig, ConfigNames extends string = keyof DefaultConfigNamesMap>(...configs: ResolvableFlatConfig<Linter.FlatConfig extends T ? T : Linter.FlatConfig>[]): FlatConfigComposer<Linter.FlatConfig extends T ? T : Linter.FlatConfig, ConfigNames>;
/**
 * The underlying impolementation of `composer()`.
 */
declare class FlatConfigComposer<T extends object = Linter.FlatConfig, ConfigNames extends string = keyof DefaultConfigNamesMap> extends Promise<T[]> {
    private _operations;
    private _operationsOverrides;
    private _operationsResolved;
    private _renames;
    constructor(...configs: ResolvableFlatConfig<T>[]);
    /**
     * Set plugin renames, like `n` -> `node`, `import-x` -> `import`, etc.
     *
     * This will runs after all config items are resolved. Applies to `plugins` and `rules`.
     */
    renamePlugins(renames: Record<string, string>): this;
    /**
     * Append configs to the end of the current configs array.
     */
    append(...items: ResolvableFlatConfig<T>[]): this;
    /**
     * Prepend configs to the beginning of the current configs array.
     */
    prepend(...items: ResolvableFlatConfig<T>[]): this;
    /**
     * Insert configs before a specific config.
     */
    insertBefore(nameOrIndex: StringLiteralUnion<ConfigNames, string | number>, ...items: ResolvableFlatConfig<T>[]): this;
    /**
     * Insert configs after a specific config.
     */
    insertAfter(nameOrIndex: StringLiteralUnion<ConfigNames, string | number>, ...items: ResolvableFlatConfig<T>[]): this;
    /**
     * Provide overrides to a specific config.
     *
     * It will be merged with the original config, or provide a custom function to replace the config entirely.
     */
    override(nameOrIndex: StringLiteralUnion<ConfigNames, string | number>, config: T | ((config: T) => Awaitable<T>)): this;
    /**
     * Provide overrides to multiple configs as an object map.
     *
     * Same as calling `override` multiple times.
     */
    overrides(overrides: Partial<Record<StringLiteralUnion<ConfigNames, string | number>, T | ((config: T) => Awaitable<T>)>>): this;
    /**
     * Override rules and it's options in **all configs**.
     *
     * Pass `null` as the value to remove the rule.
     *
     * @example
     * ```ts
     * composer
     *   .overrideRules({
     *      'no-console': 'off',
     *      'no-unused-vars': ['error', { vars: 'all', args: 'after-used' }],
     *      // remove the rule from all configs
     *      'no-undef': null,
     *   })
     * ```
     */
    overrideRules(rules: NullableObject<GetRuleRecordFromConfig<T>>): this;
    /**
     * Remove rules from **all configs**.
     *
     * @example
     * ```ts
     * composer
     *  .removeRules(
     *    'no-console',
     *    'no-unused-vars'
     *  )
     * ```
     */
    removeRules(...rules: StringLiteralUnion<FilterType<keyof GetRuleRecordFromConfig<T>, string>, string>[]): this;
    /**
     * Remove a specific config by name or index.
     */
    remove(nameOrIndex: ConfigNames | string | number): this;
    /**
     * Replace a specific config by name or index.
     *
     * The original config will be removed and replaced with the new one.
     */
    replace(nameOrIndex: StringLiteralUnion<ConfigNames, string | number>, ...items: ResolvableFlatConfig<T>[]): this;
    /**
     * Hook when all configs are resolved but before returning the final configs.
     *
     * You can modify the final configs here.
     */
    onResolved(callback: (configs: T[]) => Awaitable<T[] | void>): this;
    /**
     * Clone the composer object.
     */
    clone(): FlatConfigComposer<T>;
    /**
     * Resolve the pipeline and return the final configs.
     *
     * This returns a promise. Calling `.then()` has the same effect.
     */
    toConfigs(): Promise<T[]>;
    then(onFulfilled: (value: T[]) => any, onRejected?: (reason: any) => any): Promise<any>;
    catch(onRejected: (reason: any) => any): Promise<any>;
    finally(onFinally: () => any): Promise<T[]>;
}
/**
 * @deprecated Renamed to `composer`.
 */
declare const pipe: typeof composer;
/**
 * @deprecated Renamed to `FlatConfigComposer`.
 */
declare class FlatConfigPipeline<T extends object = Linter.FlatConfig, ConfigNames extends string = string> extends FlatConfigComposer<T, ConfigNames> {
}

/**
 * Extend another flat configs and rename globs paths.
 *
 * @example
 * ```ts
 * import { extend } from 'eslint-flat-config-utils'
 *
 * export default [
 *   ...await extend(
 *     // configs to extend
 *     import('./other-configs/eslint.config.js').then(m => m.default),
 *     // relative directory path
 *     'other-configs/',
 *   ),
 * ]
 * ```
 */
declare function extend(configs: Awaitable<Linter.FlatConfig[]>, relativePath: string): Promise<Linter.FlatConfig[]>;

export { type Arrayable, type Awaitable, type DefaultConfigNamesMap, type FilterType, FlatConfigComposer, type FlatConfigItem, FlatConfigPipeline, type GetRuleRecordFromConfig, type NullableObject, type ResolvableFlatConfig, type StringLiteralUnion, composer, concat, defineFlatConfig, extend, mergeConfigs, pipe, renamePluginsInConfigs, renamePluginsInRules };
