import { Linter } from 'eslint';

type Awaitable<T> = Promise<T> | T;
interface ValidTestCaseBase<RuleOptions = any, MessageId extends string = string> extends CompatConfigOptions, RuleTesterBehaviorOptions {
    name?: string;
    description?: string;
    code: string;
    options?: RuleOptions;
    filename?: string;
    only?: boolean;
    skip?: boolean;
    before?: (this: NormalizedTestCase<RuleOptions, MessageId>, configs: Linter.Config[]) => Awaitable<void>;
    after?: (this: NormalizedTestCase<RuleOptions, MessageId>, result: Linter.FixReport) => Awaitable<void>;
    /**
     * @deprecated Use `after` instead
     */
    onResult?: (result: Linter.FixReport) => void;
}
type TestCaseError<MessageId extends string = string> = Partial<Linter.LintMessage> & {
    /**
     * Data for interpolate the error message
     */
    data?: Record<string, any>;
    /**
     * Alias to `nodeType`
     */
    type?: string;
    messageId?: MessageId;
};
interface InvalidTestCaseBase<RuleOptions = any, MessageId extends string = string> extends ValidTestCaseBase<RuleOptions, MessageId> {
    /**
     * Expected errors.
     * If a number is provided, it asserts that the number of errors is equal to the number provided.
     * If an array of strings is provided, it asserts that the error messageIds are equal to the array provided.
     * If an array of objects is provided, it asserts that the errors are partially equal to the objects provided.
     */
    errors?: number | (MessageId | TestCaseError<MessageId>)[] | ((errors: Linter.LintMessage[]) => Awaitable<void>);
    /**
     * Assert if output is expected.
     * Pass `null` to assert that the output is the same as the input.
     */
    output?: string | null | ((output: string, input: string) => Awaitable<void>);
}
interface NormalizedTestCase<RuleOptions = any, MessageId extends string = string> extends InvalidTestCaseBase<RuleOptions, MessageId> {
    type: 'valid' | 'invalid';
    code: string;
}
type InvalidTestCase<RuleOptions = any, MessageId extends string = string> = InvalidTestCaseBase<RuleOptions, MessageId> | string;
type ValidTestCase<RuleOptions = any> = ValidTestCaseBase<RuleOptions> | string;
type TestCase<RuleOptions = any, MessageId extends string = string> = ValidTestCase<RuleOptions> | InvalidTestCase<RuleOptions, MessageId>;
interface TestExecutionResult extends Linter.FixReport {
    /**
     * If the rule fixes in multiple steps, each step will be present here
     */
    steps: Linter.FixReport[];
}
interface CompatConfigOptions {
    parserOptions?: Linter.ParserOptions;
    parser?: Linter.Parser;
    languageOptions?: Linter.Config['languageOptions'];
    linterOptions?: Linter.Config['linterOptions'];
    settings?: Linter.Config['settings'];
    processor?: Linter.Config['processor'];
    files?: Linter.Config['files'];
}
type RuleModule = any;
interface RuleTester<RuleOptions = any, MessageId extends string = string> {
    /**
     * Run a single test case
     */
    each: (arg: TestCase<RuleOptions, MessageId>) => Promise<{
        testcase: NormalizedTestCase<RuleOptions, MessageId>;
        result: TestExecutionResult;
    }>;
    /**
     * Run a single valid test case
     */
    valid: (arg: ValidTestCase<RuleOptions>) => Promise<{
        testcase: NormalizedTestCase<RuleOptions, MessageId>;
        result: TestExecutionResult;
    }>;
    /**
     * Run a single invalid test case
     */
    invalid: (arg: InvalidTestCase<RuleOptions, MessageId>) => Promise<{
        testcase: NormalizedTestCase<RuleOptions, MessageId>;
        result: TestExecutionResult;
    }>;
    /**
     * ESLint's RuleTester style test runner, that runs multiple test cases
     */
    run: (options: TestCasesOptions<RuleOptions, MessageId>) => Promise<void>;
}
interface RuleTesterBehaviorOptions {
    /**
     * The number of times to recursively apply the rule
     * @default 10
     */
    recursive?: number | false;
    /**
     * Run verification after applying the fix
     * @default true
     */
    verifyAfterFix?: boolean;
    /**
     * Verify that fix allways changes the code
     * @default true
     */
    verifyFixChanges?: boolean;
}
interface DefaultFilenames {
    js: string;
    jsx: string;
    ts: string;
    tsx: string;
}
interface RuleTesterInitOptions extends CompatConfigOptions, RuleTesterBehaviorOptions {
    /**
     * The rule to test
     */
    rule?: RuleModule;
    /**
     * The name of the rule to test
     */
    name?: string;
    /**
     * Additional flat configs to be merged with the rule config
     */
    configs?: Linter.Config | Linter.Config[];
    /**
     * The default filenames to use for type-aware tests.
     * @default { js: 'file.js', jsx: 'file.jsx', ts: 'file.ts', tsx: 'file.tsx' }
     */
    defaultFilenames?: Partial<DefaultFilenames>;
}
interface TestCasesOptions<RuleOptions = any, MessageId extends string = string> {
    valid?: (ValidTestCase<RuleOptions> | string)[];
    invalid?: (InvalidTestCase<RuleOptions, MessageId> | string)[];
    /**
     * Callback to be called after each test case
     */
    onResult?: (_case: NormalizedTestCase<RuleOptions, MessageId>, result: Linter.FixReport) => void | Promise<void>;
}

declare function pickFlatConfigFromOptions(options: CompatConfigOptions): Linter.Config | undefined;

declare function createRuleTester<RuleOptions = any, MessageId extends string = string>(options: RuleTesterInitOptions): RuleTester<RuleOptions, MessageId>;

/**
 * Shortcut to run test cases for a rule
 */
declare function run<RuleOptions = any, MessageId extends string = string>(options: TestCasesOptions<RuleOptions, MessageId> & RuleTesterInitOptions): Promise<void>;
/**
 * Shortcut to run test cases for a rule in classic style
 */
declare function runClassic<RuleOptions = any, MessageId extends string = string>(ruleName: string, rule: RuleModule, cases: TestCasesOptions<RuleOptions, MessageId>, options?: RuleTesterInitOptions): Promise<void>;

/**
 * Remove common leading whitespace from a template string.
 * Will also remove empty lines at the beginning and end.
 * @category string
 * @example
 * ```ts
 * const str = unindent`
 *   if (a) {
 *     b()
 *   }
 * `
 */
declare function unindent(str: TemplateStringsArray | string): string;

declare function normalizeTestCase<RuleOptions = any, MessageId extends string = string>(c: TestCase<RuleOptions, MessageId>, languageOptions: Linter.Config['languageOptions'], defaultFilenames: DefaultFilenames, type?: 'valid' | 'invalid'): NormalizedTestCase<RuleOptions, MessageId>;
declare function normalizeCaseError<MessageId extends string = string>(error: TestCaseError<MessageId> | MessageId, rule?: RuleModule): Partial<Linter.LintMessage>;
declare function isUsingTypeScriptParser(languageOptions: Linter.Config['languageOptions']): boolean;
declare function isUsingTypeScriptTypings(languageOptions: Linter.Config['languageOptions']): boolean;

export { unindent as $, createRuleTester, isUsingTypeScriptParser, isUsingTypeScriptTypings, normalizeCaseError, normalizeTestCase, pickFlatConfigFromOptions, run, runClassic, unindent };
export type { Awaitable, CompatConfigOptions, DefaultFilenames, InvalidTestCase, InvalidTestCaseBase, NormalizedTestCase, RuleModule, RuleTester, RuleTesterBehaviorOptions, RuleTesterInitOptions, TestCase, TestCaseError, TestCasesOptions, TestExecutionResult, ValidTestCase, ValidTestCaseBase };
