import { Lens } from '../lens';
import type { AnyOp, Update } from '../operation';
import type { PathType, Root } from '../path';
import { Path } from '../path';
import { View } from '../view';
import type { TaskArgs, TaskOp } from './context';
import { Context } from './context';
import type { Action, Instruction, Method } from './instructions';
import { MethodExpansion } from './instructions';
import type { ActionTaskProps, ContextWithSystem, MethodTaskProps } from './props';
interface TaskSpec<TState = unknown, TPath extends PathType = Root, TOp extends AnyOp = Update> {
    /**
     * A unique identifier for the task. This is automatically generated
     * when constructing he task, and is not user configurable.
     */
    readonly id: string;
    /**
     * The path to the element that this task applies to
     */
    readonly lens: Path<TPath>;
    /**
     * The operation that this task applies to
     */
    readonly op: TOp;
    /**
     * A descriptor for this task. The descriptor can be either a string or a Context
     * instance. The description does not receive the current state to allow actions to
     * be compared by their description (useful for testing and debugging).
     */
    readonly description: string | ((c: Context<TState, TPath, TOp>) => string);
    /**
     * A condition that must be met for the task to be chosen by the planner or executed by
     * an agent.
     */
    readonly condition: (s: Lens<TState, TPath>, ctx: ContextWithSystem<TState, TPath, TOp>) => boolean;
}
/**
 * An action task defines a primitive operation that can be chosen by a planner and
 * executed by an agent. Action tasks can be used to perform composite behaviors via
 * methods.
 *
 * Action tasks can be created via the Task constructor as follows
 *
 * ```typescript
 * const plusOne = Task.from({
 *   // A condition for chosing/executing the task
 *   condition: (state: number, { target }) => state < target,
 *   // The effect of the action is increasing the system
 *   // counter by 1. This will be used during planning
 *   effect: (state: View<number>) => ++state._,
 *   // The actual action that will be executed
 *   action: async (state: View<number>) => {
 *      ++state._;
 *   },
 *   // An optional description. Useful for testing
 *   description: '+1',
 * });
 * ```
 *
 * An ActionTask is also a function that binds the task to a specific context. This allows
 * the taks to be used in methods.
 *
 * ```ts
 * const plusTwo = Task.from<number>({
 *   // We want this method to be chosen only if the difference between the current
 *   // state and the target is bigger than one
 *   condition: (state, { target }) => target - state > 1,
 *   // The method returns two instances of the plusOne task bound to the given target
 *   method: (_, { target }) => [plusOne({ target }), plusOne({ target })],
 *   description: '+2',
 * });
 * ```
 */
export interface ActionTask<TState = unknown, TPath extends PathType = Root, TOp extends AnyOp = Update> extends TaskSpec<TState, TPath, TOp> {
    /**
     * The effect on the state that the task performs.
     *
     * The effect function will only be ran if the condition is met and developers
     * can trust that the condition will be checked beforehand.
     *
     * The effect function receives a view to the state, which allows
     * it to mutate the state. The effect function should not return
     * anything.
     *
     * If the task operation is `create`, the task constructor will add as
     * condition that the property pointed by the lens is undefined. The value will
     * be created before calling the effect function provided by the user.
     *
     * If the task operation is `delete`, the task constructor will add as a condition
     * that the property pointed by the lens is not undefined. The value will be deleted
     * automatically after the effect function provided by the user.
     *
     * @param view - A view to the state pointed by the lens.
     * @param ctx -	The calling context for the task. It includes any lens properties and the system object
     */
    readonly effect: (view: View<TState, TPath, TOp>, ctx: ContextWithSystem<TState, TPath, TOp>) => void;
    /**
     * TThe actual action the task performs.
     *
     * The action function will only be ran if the condition is met and developers
     * can trust that the condition will be checked beforehand.
     *
     * The action function receives a view to the state, which allows
     * it to mutate the state. The action function should not return
     * anything.
     *
     * If the task operation is `create`, the task constructor will add as
     * condition that the property pointed by the lens is undefined. The value will
     * be created before calling the action function provided by the user.
     *
     * If the task operation is `delete`, the task constructor will add as a condition
     * that the property pointed by the lens is not undefined. The value will be deleted
     * automatically after the action functions provided by the user.
     *
     * @param view - A view to the state pointed by the lens.
     * @param ctx -	The calling context for the task. It includes any lens properties and the system object
     */
    readonly action: (view: View<TState, TPath, TOp>, ctx: ContextWithSystem<TState, TPath, TOp>) => Promise<void>;
    /**
     * The task function binds the task to a context
     *
     * Grounding the task converts the specification into an instruction, that is,
     * something that can be evaluated by the planner. It does this by
     * contextualizing the task for a specific target.
     *
     * ActionTask --- ground --> Action
     * MethodTask --- ground --> Method
     */
    (ctx: TaskArgs<TState, TPath, TOp>): Action<TState, TPath, TOp>;
}
/**
 * A method task defines a composite operation that can be chosen by a planner.
 *
 * A method can guide the planner towards following a certain path by providing
 * a sequence of operations to be used if conditions are suitable.
 *
 * Method tasks can be created via the Task constructor as follows
 *
 * ```ts
 * const plusTwo = Task.from<number>({
 *   // We want this method to be chosen only if the difference between the current
 *   // state and the target is bigger than one
 *   condition: (state, { target }) => target - state > 1,
 *   // The method returns two instances of the plusOne task bound to the given target
 *   method: (_, { target }) => [plusOne({ target }), plusOne({ target })],
 *   description: '+2',
 * });
 * ```
 *
 * Methods can also reference methods for hierarchical task definition.
 */
export interface MethodTask<TState = unknown, TPath extends PathType = Root, TOp extends AnyOp = Update> extends TaskSpec<TState, TPath, TOp> {
    /**
     * The method expansion. The default is 'detect', meaning the planner will try to execute
     * the instructions returned by the method in parallel and go back to sequential expansion
     * if conflicts are detected. If sequential is chosen, the planner will jump straight to
     * sequential expansion. This is a workaround to handle those cases where detection may fail
     * due to instructios that read data handled by a parallel branch.
     */
    readonly expansion: MethodExpansion;
    /**
     * The method to be called when the task is executed.
     *
     * The method should return a list of instructions to be used by the planner.
     * It should never modify the state object.
     *
     * if the method returns an empty list, this means there are no
     * further instructions that can be applied
     */
    readonly method: (s: Lens<TState, TPath>, c: ContextWithSystem<TState, TPath, TOp>) => Instruction<TState> | Array<Instruction<TState>>;
    /**
     * The task function grounds the task
     *
     * Grounding the task converts the specification into an instruction, that is,
     * something that can be evaluated by the planner. It does this by
     * contextualizing the task for a specific target.
     *
     * ActionTask --- ground --> Action
     * MethodTask --- ground --> Method
     */
    (ctx: TaskArgs<TState, TPath, TOp>): Method<TState, TPath, TOp>;
}
/**
 * Check if a task is a method
 */
declare function isMethodTask<TState = unknown, TPath extends PathType = Root, TOp extends TaskOp = 'update'>(t: Task<TState, TPath, TOp>): t is MethodTask<TState, TPath, TOp>;
/**
 * Check if a task or an instruction is an action
 */
declare function isActionTask<TState = unknown, TPath extends PathType = Root, TOp extends TaskOp = 'update'>(t: Task<TState, TPath, TOp>): t is ActionTask<TState, TPath, TOp>;
/**
 * A task is base unit of knowledge of an autonomous agent.
 */
export type Task<TState = unknown, TPath extends PathType = Root, TOp extends TaskOp = 'update'> = ActionTask<TState, TPath, TOp> | MethodTask<TState, TPath, TOp>;
/**
 * Construct a new task (action or method) from a task specification
 */
declare function from<TState = unknown, TPath extends PathType = Root, TOp extends AnyOp = Update>(t: MethodTaskProps<TState, TPath, TOp>): MethodTask<TState, TPath, TOp>;
declare function from<TState = unknown, TPath extends PathType = Root, TOp extends AnyOp = Update>(t: ActionTaskProps<TState, TPath, TOp>): ActionTask<TState, TPath, TOp>;
interface TaskBuilder<TState> {
    from<TPath extends PathType = Root, TOp extends TaskOp = 'update'>(t: ActionTaskProps<TState, TPath, TOp>): ActionTask<TState, TPath, TOp>;
    from<TPath extends PathType = Root, TOp extends TaskOp = 'update'>(t: MethodTaskProps<TState, TPath, TOp>): MethodTask<TState, TPath, TOp>;
}
declare function of<TState>(): TaskBuilder<TState>;
export declare const Task: {
    of: typeof of;
    from: typeof from;
    isMethod: typeof isMethodTask;
    isAction: typeof isActionTask;
};
export {};
