import type { Files } from './files';
import type { Pipe } from './index';
import type { AbsolutePath } from './paths';
/**
 * A type describing the ultimate result of a {@link Plug}, {@link Pipe} or
 * {@link Task}, that is either a {@link Files} instance or `undefined`.
 */
export type Result = Files | undefined;
/**
 * The {@link State} interface defines a component tracking the current
 * _state_ of a build, caching the result of {@link Task}s, and tracking their
 * invocation stack to avoid infinite loops.
 */
export interface State {
    /** The cache of the result of {@link Task}s execution */
    readonly cache: Map<Task, Promise<Result>>;
    /** The current {@link Task} invocation stack (to avoid infinite loops) */
    readonly stack: Task[];
    /** All {@link Tasks} available in this {@link State} */
    readonly tasks: Tasks;
    /** All _properties_ available in this {@link State} */
    readonly props: Props;
    /** All _tasks_ that have failed in this {@link State} */
    readonly fails: Set<Task>;
}
/**
 * The {@link Task} interface normalizes a task definition, associating it with
 * its build file, its sibling {@link Task}s and available _properties_.
 */
export interface Task<T extends Result = Result> {
    /** The unique ID of this {@link Task} */
    readonly id: number;
    /** The _original_ name of this task */
    readonly name: string;
    /** All _properties_ siblings to this {@link Task} */
    readonly props: Props;
    /** All {@link Tasks} sibling to this {@link Task} */
    readonly tasks: Tasks;
    /** The absolute file name where this {@link Task} was defined */
    readonly buildFile: AbsolutePath;
    /** Other {@link Task}s hooked _before_ this one */
    readonly before: Task[];
    /** Other {@link Task}s hooked _after_ this one */
    readonly after: Task[];
    /** Invoke a task from in the context of a {@link Build} */
    invoke(state: State, taskName: string): Promise<T>;
}
/**
 * The {@link TaskResult} type identifies _what_ can be returned by a
 * {@link TaskDef | _task definition_}.
 */
export type TaskResult = Pipe | Files | void | undefined;
/** The {@link TaskDef} type identifies the _definition_ of a task. */
export type TaskDef<R extends TaskResult = TaskResult> = () => R | Promise<R>;
/** A callable, compiled {@link Task} from a {@link TaskDef} */
export type TaskCall<D extends BuildDef = BuildDef, R extends Result = Result> = {
    (props?: Partial<Props<D>>): Promise<R>;
    task: Task<R>;
};
/** A type identifying all _properties_ of a {@link Build}. */
export type Props<D extends BuildDef = BuildDef> = {
    readonly [k in string & keyof D as D[k] extends string ? k : never]: string;
};
/** A type identifying all _tasks_ in a {@link Build} */
export type Tasks<D extends BuildDef = BuildDef> = {
    readonly [k in string & keyof D as D[k] extends TaskDef | TaskCall ? k : never]: D[k] extends TaskDef<infer R> ? R extends void | undefined ? TaskCall<D, undefined> : R extends Pipe | Files ? TaskCall<D, Files> : never : D[k] extends TaskCall ? D[k] : never;
};
/**
 * The {@link BuildDef} interface describes the _definition_ of a {@link Build},
 * all its properties and tasks.
 */
export interface BuildDef {
    [k: string]: string | TaskDef | TaskCall;
}
/**
 * The type that will be used for `this` when invoking
 * {@link TaskDef | task definitions }.
 */
export type ThisBuild<D extends BuildDef> = {
    readonly [k in keyof D as k extends string ? k : never]: D[k] extends TaskDef<infer R> ? R extends Promise<undefined> | void | undefined ? () => Promise<undefined> : R extends Pipe | Files ? () => Pipe : never : D[k] extends TaskCall<any, infer R> ? R extends undefined ? () => Promise<undefined> : R extends Files ? () => Pipe : never : D[k] extends string ? string : never;
};
/**
 * The {@link Build} type represents the collection of {@link Task}s
 * and _properties_ compiled from a {@link BuildDef | build definition}.
 */
export type Build<D extends BuildDef = BuildDef> = Props<D> & Tasks<D>;
/** A type identifying all _task names_ in a {@link Build}. */
export type BuildTasks<B extends Build> = string & keyof {
    [name in keyof B as B[name] extends (() => any) ? name : never]: any;
};
/** A type identifying a subset of _properties_ for a {@link Build}. */
export type BuildProps<B extends Build> = {
    [name in keyof B as B[name] extends string ? name : never]?: string;
};
