import { ExclusiveKeys, ParametersExceptFirst, SendEventPayload } from "../helpers/types.js";
import { StepFetch } from "./Fetch.js";
import { InngestApi } from "../api/api.js";
import { Jsonify } from "../helpers/jsonify.js";
import { DurationLike, InstantLike, ZonedDateTimeLike } from "../helpers/temporal.js";
import { GroupTools } from "./InngestGroupTools.js";
import { Middleware } from "./middleware/middleware.js";
import { Realtime } from "./realtime/types.js";
import { EventType } from "./triggers/triggers.js";
import { MetadataStepTool, metadataSymbol } from "./InngestMetadata.js";
import { InngestExecution } from "./execution/InngestExecution.js";
import { ApplyAllMiddlewareTransforms, Context, EventPayload, HashedOp, InvocationResult, InvokeTargetFunctionDefinition, OutgoingOp, SendEventOutput, StepOptions, StepOptionsOrId, TriggerEventFromFunction } from "../types.js";
import { ClientOptionsFromInngest, GetFunctionOutputRaw, GetStepTools, Inngest } from "./Inngest.js";
import { z } from "zod/v3";
import { AiAdapter, models } from "@inngest/ai";
import { StandardSchemaV1 } from "@standard-schema/spec";

//#region src/components/InngestStepTools.d.ts

/**
 * Middleware context for a step, created during step registration.
 *
 * Uses a "deferred handler" pattern: the `wrapStep` middleware chain starts
 * during discovery (so middleware can inject its own steps), but the real
 * handler isn't known until after the memoization lookup. `setActualHandler`
 * bridges the gap — the chain blocks on a deferred promise that is resolved
 * once `executeStep` determines the real result.
 */
interface StepMiddlewareContext {
  /**
   * Sets the handler that the middleware pipeline will eventually call.
   * Called after memoization lookup to set either:
   * - A handler returning memoized data, OR
   * - A handler executing the step fresh
   */
  setActualHandler: (handler: () => Promise<unknown>) => void;
  /**
   * Step info after middleware transformations. The `options.id` may differ
   * from the original if middleware modified it via `transformStepInput`.
   */
  stepInfo: Middleware.StepInfo;
  /**
   * The middleware pipeline entry point. Call this to execute the step
   * through all middleware transformations.
   */
  wrappedHandler: () => Promise<unknown>;
}
interface FoundStep extends HashedOp {
  hashedId: string;
  fn?: (...args: unknown[]) => unknown;
  rawArgs: unknown[];
  /**
   * A boolean representing whether the step has been fulfilled, either
   * resolving or rejecting the `Promise` returned to userland code.
   *
   * Note that this is distinct from {@link hasStepState}, which instead tracks
   * whether the step has been given some state from the Executor. State from
   * the Executor could be data other than a resolution or rejection, such as
   * inputs.
   */
  fulfilled: boolean;
  /**
   * A boolean representing whether the step has been given some state from the
   * Executor. State from the Executor could be data other than a resolution or
   * rejection, such as inputs.
   *
   * This is distinct from {@link fulfilled}, which instead tracks whether the
   * step has been fulfilled, either resolving or rejecting the `Promise`
   * returned to userland code.
   */
  hasStepState: boolean;
  handled: boolean;
  /**
   * The promise that has been returned to userland code for this step.
   */
  promise: Promise<unknown>;
  /**
   * Returns a boolean representing whether or not the step was handled on this
   * invocation.
   */
  handle: () => boolean;
  input?: unknown;
  /**
   * Middleware context for this step. Holds the `wrapStep` chain entry point
   * and the deferred handler setter used by `executeStep`.
   */
  middleware: StepMiddlewareContext;
  /**
   * For new steps where wrappedHandler is called during discovery,
   * this holds the resolve/reject to be called when the step's data is
   * memoized. Resolved with server-transformed data (post-wrapStepHandler),
   * which unblocks wrapStep's `next()`.
   *
   * Is undefined when any of the following is true:
   * - The step is fulfilled
   * - The step has no handler (`step.sleep`, `step.waitForSignal`, etc.)
   */
  memoizationDeferred?: {
    resolve: (value: unknown) => void;
    reject: (error: unknown) => void;
  };
  /**
   * For new steps where `wrappedHandler` is called during discovery, this holds
   * the promise for the wrapStep-transformed result. In checkpointing mode,
   * handle() reuses this promise to avoid a duplicate wrapStep call.
   */
  transformedResultPromise?: Promise<unknown>;
  /**
   * Optional metadata updates attached to this step, carried through from
   * the OutgoingOp so that checkpoint payloads include metadata.
   */
  metadata?: OutgoingOp["metadata"];
}
type MatchOpFn<T extends (...args: unknown[]) => Promise<unknown> = (...args: unknown[]) => Promise<unknown>> = (stepOptions: StepOptions,
/**
 * Arguments passed by the user.
 */
...args: ParametersExceptFirst<T>) => Omit<HashedOp, "data" | "error">;
type StepHandler = (info: {
  matchOp: MatchOpFn;
  opts?: StepToolOptions;
  args: [StepOptionsOrId, ...unknown[]];
}) => Promise<unknown>;
interface StepToolOptions<T extends (...args: unknown[]) => Promise<unknown> = (...args: unknown[]) => Promise<unknown>> {
  /**
   * Optionally, we can also provide a function that will be called when
   * Inngest tells us to run this operation.
   *
   * If this function is defined, the first time the tool is used it will
   * report the desired operation (including options) to the Inngest. Inngest
   * will then call back to the function to tell it to run the step and then
   * retrieve data.
   *
   * We do this in order to allow functionality such as per-step retries; this
   * gives the SDK the opportunity to tell Inngest what it wants to do before
   * it does it.
   *
   * This function is passed the arguments passed by the user. It will be run
   * when we receive an operation matching this one that does not contain a
   * `data` property.
   */
  fn?: (...args: [Context.Any, ...Parameters<T>]) => unknown;
}
/**
 * Create a new set of step function tools ready to be used in a step function.
 * This function should be run and a fresh set of tools provided every time a
 * function is run.
 *
 * An op stack (function state) is passed in as well as some mutable properties
 * that the tools can use to submit a new op.
 */
/**
 * Merge client-level and function-level middleware into a single array type
 * for use with ApplyAllMiddlewareTransforms etc.
 */
type MergedMiddleware<TClient extends Inngest.Any, TFnMiddleware extends Middleware.Class[] | undefined> = [...(ClientOptionsFromInngest<TClient>["middleware"] extends Middleware.Class[] ? ClientOptionsFromInngest<TClient>["middleware"] : []), ...(TFnMiddleware extends Middleware.Class[] ? TFnMiddleware : [])];
declare const createStepTools: <TClient extends Inngest.Any, TFnMiddleware extends Middleware.Class[] | undefined = undefined>(client: TClient, execution: InngestExecution, stepHandler: StepHandler) => {
  /**
   * Send one or many events to Inngest. Should always be used in place of
   * `inngest.send()` to ensure that the event send is successfully retried
   * and not sent multiple times due to memoisation.
   *
   * @example
   * ```ts
   * await step.sendEvent("emit-user-creation", {
   *   name: "app/user.created",
   *   data: { id: 123 },
   * });
   *
   * await step.sendEvent("emit-user-updates", [
   *   {
   *     name: "app/user.created",
   *     data: { id: 123 },
   *   },
   *   {
   *     name: "app/user.feed.created",
   *     data: { id: 123 },
   *   },
   * ]);
   * ```
   *
   * Returns a promise that will resolve once the event has been sent.
   */
  sendEvent: (idOrOptions: StepOptionsOrId, payload: SendEventPayload) => Promise<SendEventOutput<ClientOptionsFromInngest<TClient>>>;
  /**
   * EXPERIMENTAL: This API is not yet stable and may change in the future
   * without a major version bump.
   *
   * Wait for a particular signal to be received before continuing. When the
   * signal is received, its data will be returned.
   */
  waitForSignal: <TData>(idOrOptions: StepOptionsOrId, opts: WaitForSignalOpts) => Promise<{
    signal: string;
    data: Jsonify<TData>;
  } | null>;
  /**
   * Step-level functionality related to realtime features.
   *
   * Unlike client-level realtime methods (`inngest.realtime.*`), these tools
   * will be their own durable steps when run. If you wish to use realtime
   * features outside of a step, make sure to use the client-level methods
   * instead.
   */
  realtime: {
    /**
     * Publish a realtime message as a durable step. Memoized and will not
     * re-fire on retry, unlike client-level `inngest.realtime.publish()`.
     *
     * Uses topic accessors: `step.realtime.publish("id", chat.status, data)`
     */
    publish: <TData>(idOrOptions: StepOptionsOrId, topicRef: Realtime.TopicRef<TData>, data: TData) => Promise<TData>;
  };
  /**
   * Send a Signal to Inngest.
   */
  sendSignal: (idOrOptions: StepOptionsOrId, opts: SendSignalOpts) => Promise<InngestApi.SendSignalResponse>;
  /**
   * Wait for a particular event to be received before continuing. When the
   * event is received, it will be returned.
   *
   * You can also provide options to control the particular event that is
   * received, for example to ensure that a user ID matches between two
   * events, or to only wait a maximum amount of time before giving up and
   * returning `null` instead of any event data.
   */
  waitForEvent: <TOpts extends {
    /**
     * The event to wait for.
     */
    event: string | EventType<string, any>;
    /**
     * The step function will wait for the event for a maximum of this
     * time, at which point the signal will be returned as `null` instead
     * of any signal data.
     *
     * The time to wait can be specified using a `number` of milliseconds,
     * an `ms`-compatible time string like `"1 hour"`, `"30 mins"`, or
     * `"2.5d"`, a `Date`, a `Temporal.Duration` (relative wait), or a
     * `Temporal.Instant` / `Temporal.ZonedDateTime` (absolute deadline).
     *
     * {@link https://npm.im/ms}
     */
    timeout: number | string | Date | DurationLike | InstantLike | ZonedDateTimeLike;
  } & ExclusiveKeys<{
    match?: string;
    if?: string;
  }, "match", "if">>(idOrOptions: StepOptionsOrId, opts: TOpts) => Promise<WaitForEventResult<TOpts>>;
  /**
   * Use this tool to run business logic. Each call to `run` will be retried
   * individually, meaning you can compose complex workflows that safely
   * retry dependent asynchronous actions.
   *
   * The function you pass to `run` will be called only when this "step" is to
   * be executed and can be synchronous or asynchronous.
   *
   * In either case, the return value of the function will be the return value
   * of the `run` tool, meaning you can return and reason about return data
   * for next steps.
   */
  run: <TFn extends (...args: any[]) => unknown>(idOrOptions: StepOptionsOrId, fn: TFn, ...input: Parameters<TFn>) => Promise<ApplyAllMiddlewareTransforms<MergedMiddleware<TClient, TFnMiddleware>, TFn extends ((...args: Parameters<TFn>) => Promise<infer U>) ? Awaited<U extends void ? null : U> : ReturnType<TFn> extends void ? null : ReturnType<TFn>>>;
  /**
   * AI tooling for running AI models and other AI-related tasks.
   */
  ai: {
    /**
     * Use this tool to have Inngest make your AI calls. Useful for agentic workflows.
     *
     * Input is also tracked for this tool, meaning you can pass input to the
     * function and it will be displayed and editable in the UI.
     */
    infer: <TAdapter extends AiAdapter>(idOrOptions: StepOptionsOrId, options: AiInferOpts<TAdapter>) => Promise<AiAdapter.Output<TAdapter>>;
    /**
     * Use this tool to wrap AI models and other AI-related tasks. Each call
     * to `wrap` will be retried individually, meaning you can compose complex
     * workflows that safely retry dependent asynchronous actions.
     *
     * Input is also tracked for this tool, meaning you can pass input to the
     * function and it will be displayed and editable in the UI.
     */
    wrap: <TFn extends (...args: any[]) => unknown>(idOrOptions: StepOptionsOrId, fn: TFn, ...input: Parameters<TFn>) => Promise<ApplyAllMiddlewareTransforms<MergedMiddleware<TClient, TFnMiddleware>, TFn extends ((...args: Parameters<TFn>) => Promise<infer U>) ? Awaited<U extends void ? null : U> : ReturnType<TFn> extends void ? null : ReturnType<TFn>>>;
    /**
     * Models for AI inference and other AI-related tasks.
     */
    models: {
      anthropic: AiAdapter.ModelCreator<[options: models.Anthropic.AiModelOptions], models.Anthropic.AiModel>;
      gemini: AiAdapter.ModelCreator<[options: models.Gemini.AiModelOptions], models.Gemini.AiModel>;
      openai: AiAdapter.ModelCreator<[options: models.OpenAi.AiModelOptions], models.OpenAi.AiModel>;
      deepseek: AiAdapter.ModelCreator<[options: models.DeepSeek.AiModelOptions], models.DeepSeek.AiModel>;
      grok: AiAdapter.ModelCreator<[options: models.Grok.AiModelOptions], models.Grok.AiModel>;
    };
  };
  /**
   * Wait a specified amount of time before continuing.
   *
   * The time to wait can be specified using a `number` of milliseconds or an
   * `ms`-compatible time string like `"1 hour"`, `"30 mins"`, or `"2.5d"`.
   *
   * {@link https://npm.im/ms}
   *
   * To wait until a particular date, use `sleepUntil` instead.
   */
  sleep: (idOrOptions: StepOptionsOrId, time: number | string | DurationLike) => Promise<void>;
  /**
   * Wait until a particular date before continuing by passing a `Date`.
   *
   * To wait for a particular amount of time from now, always use `sleep`
   * instead.
   */
  sleepUntil: (idOrOptions: StepOptionsOrId, time: Date | string | InstantLike | ZonedDateTimeLike) => Promise<void>;
  /**
   * Invoke a passed Inngest `function` with the given `data`. Returns the
   * result of the returned value of the function or `null` if the function
   * does not return a value.
   */
  invoke: <TFunction extends InvokeTargetFunctionDefinition>(idOrOptions: StepOptionsOrId, opts: InvocationOpts<TFunction>) => InvocationResult<ApplyAllMiddlewareTransforms<MergedMiddleware<TClient, TFnMiddleware>, GetFunctionOutputRaw<TFunction>, "functionOutputTransform">>;
  /**
   * `step.fetch` is a Fetch-API-compatible function that can be used to make
   * any HTTP code durable if it's called within an Inngest function.
   *
   * It will gracefully fall back to the global `fetch` if called outside of
   * this context, and a custom fallback can be set using the `config` method.
   */
  fetch: StepFetch;
};
/**
 * A generic set of step tools, without typing information about the client used
 * to create them.
 */
type GenericStepTools = GetStepTools<Inngest.Any>;
type ExperimentalStepTools = GetStepTools<Inngest.Any> & {
  [metadataSymbol]: (memoizationId: string) => MetadataStepTool;
};
/**
 * A generic set of step tools that can be used without typing information about
 * the client used to create them.
 *
 * These tools use AsyncLocalStorage to track the context in which they are
 * used, and will throw an error if used outside of an Inngest context.
 *
 * The intention of these high-level tools is to allow usage of Inngest step
 * tools within API endpoints, though they can still be used within regular
 * Inngest functions as well.
 */
declare const step: GenericStepTools;
/**
 * A deferred proxy for `group` tools that delegates through ALS context.
 *
 * @public
 */
declare const group: GroupTools;
type InvocationTargetOpts<TFunction extends InvokeTargetFunctionDefinition> = {
  function: TFunction;
};
type InvocationOpts<TFunction extends InvokeTargetFunctionDefinition> = InvocationTargetOpts<TFunction> & Omit<TriggerEventFromFunction<TFunction>, "id"> & {
  /**
   * The step function will wait for the invocation to finish for a maximum
   * of this time, at which point the retured promise will be rejected
   * instead of resolved with the output of the invoked function.
   *
   * Note that the invoked function will continue to run even if this step
   * times out.
   *
   * The time to wait can be specified using a `number` of milliseconds, an
   * `ms`-compatible time string like `"1 hour"`, `"30 mins"`, or `"2.5d"`,
   * a `Date`, a `Temporal.Duration` (relative wait), or a `Temporal.Instant`
   * / `Temporal.ZonedDateTime` (absolute deadline).
   *
   * {@link https://npm.im/ms}
   */
  timeout?: number | string | Date | DurationLike | InstantLike | ZonedDateTimeLike;
};
/**
 * A set of parameters given to a `sendSignal` call.
 */
type SendSignalOpts = {
  /**
   * The signal to send.
   */
  signal: string;
  /**
   * The data to send with the signal.
   */
  data?: unknown;
};
/**
 * A set of parameters given to a `waitForSignal` call.
 */
type WaitForSignalOpts = {
  /**
   * The signal to wait for.
   */
  signal: string;
  /**
   * The step function will wait for the signal for a maximum of this time, at
   * which point the signal will be returned as `null` instead of any signal
   * data.
   *
   * The time to wait can be specified using a `number` of milliseconds, an
   * `ms`-compatible time string like `"1 hour"`, `"30 mins"`, or `"2.5d"`, a
   * `Date`, a `Temporal.Duration` (relative wait), or a `Temporal.Instant` /
   * `Temporal.ZonedDateTime` (absolute deadline).
   *
   * {@link https://npm.im/ms}
   */
  timeout: number | string | Date | DurationLike | InstantLike | ZonedDateTimeLike;
  /**
   * When this `step.waitForSignal()` call is made, choose whether an existing
   * wait for the same signal should be replaced, or whether this run should
   * fail.
   *
   * `"replace"` will replace any existing wait with this one, and the existing
   * wait will remain pending until it reaches its timeout.
   *
   * `"fail"` will cause this run to fail if there is already a wait for the
   * same signal.
   */
  onConflict: "replace" | "fail";
};
/**
 * Computes the return type for `waitForEvent` based on the options provided.
 *
 * Handles three cases:
 * 1. `event: EventType<TName, TSchema>` - extracts name and data from EventType
 * 2. `event: string` with `schema` - uses string as name and schema for data
 * 3. `event: string` without schema - uses string as name with untyped data
 */
type WaitForEventResult<TOpts> = TOpts extends {
  event: EventType<infer TName extends string, StandardSchemaV1<infer TData extends Record<string, unknown>>>;
} ? {
  name: TName;
  data: TData;
  id: string;
  ts: number;
  v?: string;
} | null : TOpts extends {
  event: EventType<infer TName extends string, undefined>;
} ? {
  name: TName;
  data: Record<string, any>;
  id: string;
  ts: number;
  v?: string;
} | null : TOpts extends {
  event: infer TName extends string;
  schema: StandardSchemaV1<infer TData extends Record<string, unknown>>;
} ? {
  name: TName;
  data: TData;
  id: string;
  ts: number;
  v?: string;
} | null : TOpts extends {
  event: infer TName extends string;
} ? {
  name: TName;
  data: Record<string, any>;
  id: string;
  ts: number;
  v?: string;
} | null : EventPayload | null;
/**
 * Options for `step.ai.infer()`.
 */
type AiInferOpts<TModel extends AiAdapter> = {
  /**
   * The model to use for the inference. Create a model by importing from
   * `"inngest"` or by using `step.ai.models.*`.
   *
   * @example Import `openai()`
   * ```ts
   * import { openai } from "inngest";
   *
   * const model = openai({ model: "gpt-4" });
   * ```
   *
   * @example Use a model from `step.ai.models`
   * ```ts
   * async ({ step }) => {
   *            const model = step.ai.models.openai({ model: "gpt-4" });
   * }
   * ```
   */
  model: TModel;
  /**
   * The input to pass to the model.
   */
  body: AiAdapter.Input<TModel>;
};
//#endregion
export { ExperimentalStepTools, FoundStep, createStepTools, group, step };
//# sourceMappingURL=InngestStepTools.d.ts.map