import { ErrorWithDiff, Awaitable } from '@vitest/utils';

interface FixtureItem extends FixtureOptions {
	prop: string;
	value: any;
	scope: "test" | "file" | "worker";
	/**
	* Indicates whether the fixture is a function
	*/
	isFn: boolean;
	/**
	* The dependencies(fixtures) of current fixture function.
	*/
	deps?: FixtureItem[];
}

type ChainableFunction<
	T extends string,
	F extends (...args: any) => any,
	C = object
> = F & { [x in T] : ChainableFunction<T, F, C> } & {
	fn: (this: Record<T, any>, ...args: Parameters<F>) => ReturnType<F>
} & C;
declare function createChainable<
	T extends string,
	Args extends any[],
	R = any
>(keys: T[], fn: (this: Record<T, any>, ...args: Args) => R): ChainableFunction<T, (...args: Args) => R>;

type RunMode = "run" | "skip" | "only" | "todo" | "queued";
type TaskState = RunMode | "pass" | "fail";
interface TaskBase {
	/**
	* Unique task identifier. Based on the file id and the position of the task.
	* The id of the file task is based on the file path relative to root and project name.
	* It will not change between runs.
	* @example `1201091390`, `1201091390_0`, `1201091390_0_1`
	*/
	id: string;
	/**
	* Task name provided by the user. If no name was provided, it will be an empty string.
	*/
	name: string;
	/**
	* Task mode.
	* - **skip**: task is skipped
	* - **only**: only this task and other tasks with `only` mode will run
	* - **todo**: task is marked as a todo, alias for `skip`
	* - **run**: task will run or already ran
	* - **queued**: task will start running next. It can only exist on the File
	*/
	mode: RunMode;
	/**
	* Custom metadata for the task. JSON reporter will save this data.
	*/
	meta: TaskMeta;
	/**
	* Whether the task was produced with `.each()` method.
	*/
	each?: boolean;
	/**
	* Whether the task should run concurrently with other tasks.
	*/
	concurrent?: boolean;
	/**
	* Whether the tasks of the suite run in a random order.
	*/
	shuffle?: boolean;
	/**
	* Suite that this task is part of. File task or the global suite will have no parent.
	*/
	suite?: Suite;
	/**
	* Result of the task. Suite and file tasks will only have the result if there
	* was an error during collection or inside `afterAll`/`beforeAll`.
	*/
	result?: TaskResult;
	/**
	* The amount of times the task should be retried if it fails.
	* @default 0
	*/
	retry?: number;
	/**
	* The amount of times the task should be repeated after the successful run.
	* If the task fails, it will not be retried unless `retry` is specified.
	* @default 0
	*/
	repeats?: number;
	/**
	* Location of the task in the file. This field is populated only if
	* `includeTaskLocation` option is set. It is generated by calling `new Error`
	* and parsing the stack trace, so the location might differ depending on the runtime.
	*/
	location?: {
		line: number
		column: number
	};
}
interface TaskPopulated extends TaskBase {
	/**
	* File task. It's the root task of the file.
	*/
	file: File;
	/**
	* Whether the task should succeed if it fails. If the task fails, it will be marked as passed.
	*/
	fails?: boolean;
	/**
	* Store promises (from async expects) to wait for them before finishing the test
	*/
	promises?: Promise<any>[];
}
/**
* Custom metadata that can be used in reporters.
*/
interface TaskMeta {}
/**
* The result of calling a task.
*/
interface TaskResult {
	/**
	* State of the task. Inherits the `task.mode` during collection.
	* When the task has finished, it will be changed to `pass` or `fail`.
	* - **pass**: task ran successfully
	* - **fail**: task failed
	*/
	state: TaskState;
	/**
	* Errors that occurred during the task execution. It is possible to have several errors
	* if `expect.soft()` failed multiple times or `retry` was triggered.
	*/
	errors?: ErrorWithDiff[];
	/**
	* How long in milliseconds the task took to run.
	*/
	duration?: number;
	/**
	* Time in milliseconds when the task started running.
	*/
	startTime?: number;
	/**
	* Heap size in bytes after the task finished.
	* Only available if `logHeapUsage` option is set and `process.memoryUsage` is defined.
	*/
	heap?: number;
	/**
	* State of related to this task hooks. Useful during reporting.
	*/
	hooks?: Partial<Record<keyof SuiteHooks, TaskState>>;
	/**
	* The amount of times the task was retried. The task is retried only if it
	* failed and `retry` option is set.
	*/
	retryCount?: number;
	/**
	* The amount of times the task was repeated. The task is repeated only if
	* `repeats` option is set. This number also contains `retryCount`.
	*/
	repeatCount?: number;
}
/** The time spent importing & executing a non-externalized file. */
interface ImportDuration {
	/** The time spent importing & executing the file itself, not counting all non-externalized imports that the file does. */
	selfTime: number;
	/** The time spent importing & executing the file and all its imports. */
	totalTime: number;
}
/**
* The tuple representing a single task update.
* Usually reported after the task finishes.
*/
type TaskResultPack = [id: string, result: TaskResult | undefined, meta: TaskMeta];
interface TaskEventData {
	annotation?: TestAnnotation | undefined;
}
type TaskEventPack = [id: string, event: TaskUpdateEvent, data: TaskEventData | undefined];
type TaskUpdateEvent = "test-failed-early" | "suite-failed-early" | "test-prepare" | "test-finished" | "test-retried" | "suite-prepare" | "suite-finished" | "before-hook-start" | "before-hook-end" | "after-hook-start" | "after-hook-end" | "test-annotation";
interface Suite extends TaskBase {
	type: "suite";
	/**
	* File task. It's the root task of the file.
	*/
	file: File;
	/**
	* An array of tasks that are part of the suite.
	*/
	tasks: Task[];
}
interface File extends Suite {
	/**
	* The name of the pool that the file belongs to.
	* @default 'forks'
	*/
	pool?: string;
	/**
	* The path to the file in UNIX format.
	*/
	filepath: string;
	/**
	* The name of the workspace project the file belongs to.
	*/
	projectName: string | undefined;
	/**
	* The time it took to collect all tests in the file.
	* This time also includes importing all the file dependencies.
	*/
	collectDuration?: number;
	/**
	* The time it took to import the setup file.
	*/
	setupDuration?: number;
	/** The time spent importing every non-externalized dependency that Vitest has processed. */
	importDurations?: Record<string, ImportDuration>;
}
interface Test<ExtraContext = object> extends TaskPopulated {
	type: "test";
	/**
	* Test context that will be passed to the test function.
	*/
	context: TestContext & ExtraContext;
	/**
	* The test timeout in milliseconds.
	*/
	timeout: number;
	/**
	* An array of custom annotations.
	*/
	annotations: TestAnnotation[];
}
interface TestAttachment {
	contentType?: string;
	path?: string;
	body?: string | Uint8Array;
}
interface TestAnnotationLocation {
	line: number;
	column: number;
	file: string;
}
interface TestAnnotation {
	message: string;
	type: string;
	location?: TestAnnotationLocation;
	attachment?: TestAttachment;
}
/**
* @deprecated Use `Test` instead. `type: 'custom'` is not used since 2.2
*/
type Custom<ExtraContext = object> = Test<ExtraContext>;
type Task = Test | Suite | File;
/**
* @deprecated Vitest doesn't provide `done()` anymore
*/
type DoneCallback = (error?: any) => void;
type TestFunction<ExtraContext = object> = (context: TestContext & ExtraContext) => Awaitable<any> | void;
// jest's ExtractEachCallbackArgs
type ExtractEachCallbackArgs<T extends ReadonlyArray<any>> = {
	1: [T[0]]
	2: [T[0], T[1]]
	3: [T[0], T[1], T[2]]
	4: [T[0], T[1], T[2], T[3]]
	5: [T[0], T[1], T[2], T[3], T[4]]
	6: [T[0], T[1], T[2], T[3], T[4], T[5]]
	7: [T[0], T[1], T[2], T[3], T[4], T[5], T[6]]
	8: [T[0], T[1], T[2], T[3], T[4], T[5], T[6], T[7]]
	9: [T[0], T[1], T[2], T[3], T[4], T[5], T[6], T[7], T[8]]
	10: [T[0], T[1], T[2], T[3], T[4], T[5], T[6], T[7], T[8], T[9]]
	fallback: Array<T extends ReadonlyArray<infer U> ? U : any>
}[T extends Readonly<[any]> ? 1 : T extends Readonly<[any, any]> ? 2 : T extends Readonly<[any, any, any]> ? 3 : T extends Readonly<[any, any, any, any]> ? 4 : T extends Readonly<[any, any, any, any, any]> ? 5 : T extends Readonly<[any, any, any, any, any, any]> ? 6 : T extends Readonly<[any, any, any, any, any, any, any]> ? 7 : T extends Readonly<[any, any, any, any, any, any, any, any]> ? 8 : T extends Readonly<[any, any, any, any, any, any, any, any, any]> ? 9 : T extends Readonly<[any, any, any, any, any, any, any, any, any, any]> ? 10 : "fallback"];
interface EachFunctionReturn<T extends any[]> {
	/**
	* @deprecated Use options as the second argument instead
	*/
	(name: string | Function, fn: (...args: T) => Awaitable<void>, options: TestCollectorOptions): void;
	(name: string | Function, fn: (...args: T) => Awaitable<void>, options?: number | TestCollectorOptions): void;
	(name: string | Function, options: TestCollectorOptions, fn: (...args: T) => Awaitable<void>): void;
}
interface TestEachFunction {
	<T extends any[] | [any]>(cases: ReadonlyArray<T>): EachFunctionReturn<T>;
	<T extends ReadonlyArray<any>>(cases: ReadonlyArray<T>): EachFunctionReturn<ExtractEachCallbackArgs<T>>;
	<T>(cases: ReadonlyArray<T>): EachFunctionReturn<T[]>;
	(...args: [TemplateStringsArray, ...any]): EachFunctionReturn<any[]>;
}
interface TestForFunctionReturn<
	Arg,
	Context
> {
	(name: string | Function, fn: (arg: Arg, context: Context) => Awaitable<void>): void;
	(name: string | Function, options: TestCollectorOptions, fn: (args: Arg, context: Context) => Awaitable<void>): void;
}
interface TestForFunction<ExtraContext> {
	// test.for([1, 2, 3])
	// test.for([[1, 2], [3, 4, 5]])
	<T>(cases: ReadonlyArray<T>): TestForFunctionReturn<T, TestContext & ExtraContext>;
	// test.for`
	//    a  |  b
	//   {1} | {2}
	//   {3} | {4}
	// `
	(strings: TemplateStringsArray, ...values: any[]): TestForFunctionReturn<any, TestContext & ExtraContext>;
}
interface SuiteForFunction {
	<T>(cases: ReadonlyArray<T>): EachFunctionReturn<[T]>;
	(...args: [TemplateStringsArray, ...any]): EachFunctionReturn<any[]>;
}
interface TestCollectorCallable<C = object> {
	/**
	* @deprecated Use options as the second argument instead
	*/
	<ExtraContext extends C>(name: string | Function, fn: TestFunction<ExtraContext>, options: TestCollectorOptions): void;
	<ExtraContext extends C>(name: string | Function, fn?: TestFunction<ExtraContext>, options?: number | TestCollectorOptions): void;
	<ExtraContext extends C>(name: string | Function, options?: TestCollectorOptions, fn?: TestFunction<ExtraContext>): void;
}
type ChainableTestAPI<ExtraContext = object> = ChainableFunction<"concurrent" | "sequential" | "only" | "skip" | "todo" | "fails", TestCollectorCallable<ExtraContext>, {
	each: TestEachFunction
	for: TestForFunction<ExtraContext>
}>;
type TestCollectorOptions = Omit<TestOptions, "shuffle">;
interface TestOptions {
	/**
	* Test timeout.
	*/
	timeout?: number;
	/**
	* Times to retry the test if fails. Useful for making flaky tests more stable.
	* When retries is up, the last test error will be thrown.
	*
	* @default 0
	*/
	retry?: number;
	/**
	* How many times the test will run again.
	* Only inner tests will repeat if set on `describe()`, nested `describe()` will inherit parent's repeat by default.
	*
	* @default 0
	*/
	repeats?: number;
	/**
	* Whether suites and tests run concurrently.
	* Tests inherit `concurrent` from `describe()` and nested `describe()` will inherit from parent's `concurrent`.
	*/
	concurrent?: boolean;
	/**
	* Whether tests run sequentially.
	* Tests inherit `sequential` from `describe()` and nested `describe()` will inherit from parent's `sequential`.
	*/
	sequential?: boolean;
	/**
	* Whether the tasks of the suite run in a random order.
	*/
	shuffle?: boolean;
	/**
	* Whether the test should be skipped.
	*/
	skip?: boolean;
	/**
	* Should this test be the only one running in a suite.
	*/
	only?: boolean;
	/**
	* Whether the test should be skipped and marked as a todo.
	*/
	todo?: boolean;
	/**
	* Whether the test is expected to fail. If it does, the test will pass, otherwise it will fail.
	*/
	fails?: boolean;
}
interface ExtendedAPI<ExtraContext> {
	skipIf: (condition: any) => ChainableTestAPI<ExtraContext>;
	runIf: (condition: any) => ChainableTestAPI<ExtraContext>;
}
type TestAPI<ExtraContext = object> = ChainableTestAPI<ExtraContext> & ExtendedAPI<ExtraContext> & {
	extend: <T extends Record<string, any> = object>(fixtures: Fixtures<T, ExtraContext>) => TestAPI<{ [K in keyof T | keyof ExtraContext] : K extends keyof T ? T[K] : K extends keyof ExtraContext ? ExtraContext[K] : never }>
	scoped: (fixtures: Fixtures<Partial<ExtraContext>>) => void
};

interface FixtureOptions {
	/**
	* Whether to automatically set up current fixture, even though it's not being used in tests.
	* @default false
	*/
	auto?: boolean;
	/**
	* Indicated if the injected value from the config should be preferred over the fixture value
	*/
	injected?: boolean;
	/**
	* When should the fixture be set up.
	* - **test**: fixture will be set up before every test
	* - **worker**: fixture will be set up once per worker
	* - **file**: fixture will be set up once per file
	*
	* **Warning:** The `vmThreads` and `vmForks` pools initiate worker fixtures once per test file.
	* @default 'test'
	*/
	scope?: "test" | "worker" | "file";
}
type Use<T> = (value: T) => Promise<void>;
type FixtureFn<
	T,
	K extends keyof T,
	ExtraContext
> = (context: Omit<T, K> & ExtraContext, use: Use<T[K]>) => Promise<void>;
type Fixture<
	T,
	K extends keyof T,
	ExtraContext = object
> = ((...args: any) => any) extends T[K] ? T[K] extends any ? FixtureFn<T, K, Omit<ExtraContext, Exclude<keyof T, K>>> : never : T[K] | (T[K] extends any ? FixtureFn<T, K, Omit<ExtraContext, Exclude<keyof T, K>>> : never);
type Fixtures<
	T extends Record<string, any>,
	ExtraContext = object
> = { [K in keyof T] : Fixture<T, K, ExtraContext & TestContext> | [Fixture<T, K, ExtraContext & TestContext>, FixtureOptions?] };
type InferFixturesTypes<T> = T extends TestAPI<infer C> ? C : T;
interface SuiteCollectorCallable<ExtraContext = object> {
	/**
	* @deprecated Use options as the second argument instead
	*/
	<OverrideExtraContext extends ExtraContext = ExtraContext>(name: string | Function, fn: SuiteFactory<OverrideExtraContext>, options: TestOptions): SuiteCollector<OverrideExtraContext>;
	<OverrideExtraContext extends ExtraContext = ExtraContext>(name: string | Function, fn?: SuiteFactory<OverrideExtraContext>, options?: number | TestOptions): SuiteCollector<OverrideExtraContext>;
	<OverrideExtraContext extends ExtraContext = ExtraContext>(name: string | Function, options: TestOptions, fn?: SuiteFactory<OverrideExtraContext>): SuiteCollector<OverrideExtraContext>;
}
type ChainableSuiteAPI<ExtraContext = object> = ChainableFunction<"concurrent" | "sequential" | "only" | "skip" | "todo" | "shuffle", SuiteCollectorCallable<ExtraContext>, {
	each: TestEachFunction
	for: SuiteForFunction
}>;
type SuiteAPI<ExtraContext = object> = ChainableSuiteAPI<ExtraContext> & {
	skipIf: (condition: any) => ChainableSuiteAPI<ExtraContext>
	runIf: (condition: any) => ChainableSuiteAPI<ExtraContext>
};
/**
* @deprecated
*/
type HookListener<
	T extends any[],
	Return = void
> = (...args: T) => Awaitable<Return>;
/**
* @deprecated
*/
type HookCleanupCallback = unknown;
interface BeforeAllListener {
	(suite: Readonly<Suite | File>): Awaitable<unknown>;
}
interface AfterAllListener {
	(suite: Readonly<Suite | File>): Awaitable<unknown>;
}
interface BeforeEachListener<ExtraContext = object> {
	(context: TestContext & ExtraContext, suite: Readonly<Suite>): Awaitable<unknown>;
}
interface AfterEachListener<ExtraContext = object> {
	(context: TestContext & ExtraContext, suite: Readonly<Suite>): Awaitable<unknown>;
}
interface SuiteHooks<ExtraContext = object> {
	beforeAll: BeforeAllListener[];
	afterAll: AfterAllListener[];
	beforeEach: BeforeEachListener<ExtraContext>[];
	afterEach: AfterEachListener<ExtraContext>[];
}
interface TaskCustomOptions extends TestOptions {
	/**
	* Whether the task was produced with `.each()` method.
	*/
	each?: boolean;
	/**
	* Custom metadata for the task that will be assigned to `task.meta`.
	*/
	meta?: Record<string, unknown>;
	/**
	* Task fixtures.
	*/
	fixtures?: FixtureItem[];
	/**
	* Function that will be called when the task is executed.
	* If nothing is provided, the runner will try to get the function using `getFn(task)`.
	* If the runner cannot find the function, the task will be marked as failed.
	*/
	handler?: (context: TestContext) => Awaitable<void>;
}
interface SuiteCollector<ExtraContext = object> {
	readonly name: string;
	readonly mode: RunMode;
	options?: TestOptions;
	type: "collector";
	test: TestAPI<ExtraContext>;
	tasks: (Suite | Test<ExtraContext> | SuiteCollector<ExtraContext>)[];
	scoped: (fixtures: Fixtures<any, ExtraContext>) => void;
	fixtures: () => FixtureItem[] | undefined;
	suite?: Suite;
	task: (name: string, options?: TaskCustomOptions) => Test<ExtraContext>;
	collect: (file: File) => Promise<Suite>;
	clear: () => void;
	on: <T extends keyof SuiteHooks<ExtraContext>>(name: T, ...fn: SuiteHooks<ExtraContext>[T]) => void;
}
type SuiteFactory<ExtraContext = object> = (test: TestAPI<ExtraContext>) => Awaitable<void>;
interface RuntimeContext {
	tasks: (SuiteCollector | Test)[];
	currentSuite: SuiteCollector | null;
}
/**
* User's custom test context.
*/
interface TestContext {
	/**
	* Metadata of the current test
	*/
	readonly task: Readonly<Test>;
	/**
	* An [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) that will be aborted if the test times out or
	* the test run was cancelled.
	* @see {@link https://vitest.dev/guide/test-context#signal}
	*/
	readonly signal: AbortSignal;
	/**
	* Extract hooks on test failed
	* @see {@link https://vitest.dev/guide/test-context#ontestfailed}
	*/
	readonly onTestFailed: (fn: OnTestFailedHandler, timeout?: number) => void;
	/**
	* Extract hooks on test failed
	* @see {@link https://vitest.dev/guide/test-context#ontestfinished}
	*/
	readonly onTestFinished: (fn: OnTestFinishedHandler, timeout?: number) => void;
	/**
	* Mark tests as skipped. All execution after this call will be skipped.
	* This function throws an error, so make sure you are not catching it accidentally.
	* @see {@link https://vitest.dev/guide/test-context#skip}
	*/
	readonly skip: {
		(note?: string): never
		(condition: boolean, note?: string): void
	};
	/**
	* Add a test annotation that will be displayed by your reporter.
	* @see {@link https://vitest.dev/guide/test-context#annotate}
	*/
	readonly annotate: {
		(message: string, type?: string, attachment?: TestAttachment): Promise<TestAnnotation>
		(message: string, attachment?: TestAttachment): Promise<TestAnnotation>
	};
}
/**
* Context that's always available in the test function.
* @deprecated use `TestContext` instead
*/
interface TaskContext extends TestContext {}
/** @deprecated use `TestContext` instead */
type ExtendedContext = TaskContext & TestContext;
type OnTestFailedHandler = (context: TestContext) => Awaitable<void>;
type OnTestFinishedHandler = (context: TestContext) => Awaitable<void>;
interface TaskHook<HookListener> {
	(fn: HookListener, timeout?: number): void;
}
type SequenceHooks = "stack" | "list" | "parallel";
type SequenceSetupFiles = "list" | "parallel";

export { createChainable as c };
export type { AfterAllListener as A, BeforeAllListener as B, ChainableFunction as C, DoneCallback as D, ExtendedContext as E, File as F, TaskMeta as G, HookCleanupCallback as H, ImportDuration as I, TaskPopulated as J, TaskResult as K, TaskResultPack as L, TaskState as M, TestAnnotation as N, OnTestFailedHandler as O, TestAnnotationLocation as P, TestAttachment as Q, RunMode as R, Suite as S, Task as T, TestContext as U, TestFunction as V, TestOptions as W, Use as X, Test as a, AfterEachListener as b, BeforeEachListener as d, TaskHook as e, OnTestFinishedHandler as f, Custom as g, SuiteHooks as h, TaskUpdateEvent as i, TestAPI as j, SuiteAPI as k, SuiteCollector as l, Fixture as m, FixtureFn as n, FixtureOptions as o, Fixtures as p, HookListener as q, InferFixturesTypes as r, RuntimeContext as s, SequenceHooks as t, SequenceSetupFiles as u, SuiteFactory as v, TaskBase as w, TaskContext as x, TaskCustomOptions as y, TaskEventPack as z };
