import { Checkpoint, Config, Metadata, ThreadState } from "../schema.js";
import { AIMessage, DefaultToolCall } from "../types.messages.js";
import { CheckpointsStreamEvent, CustomStreamEvent, DebugStreamEvent, EventsStreamEvent, MetadataStreamEvent, StreamMode, TasksStreamEvent, UpdatesStreamEvent } from "../types.stream.js";
import { Command, DisconnectMode, Durability, MultitaskStrategy, OnCompletionBehavior } from "../types.js";
import { Client, ClientConfig } from "../client.js";
import { BagTemplate } from "../types.template.js";

//#region src/ui/types.d.ts

/**
 * Minimal interface matching the structure of AgentTypeConfig from @langchain/langgraph.
 * This allows type inference from ReactAgent without requiring the langchain dependency.
 */
interface AgentTypeConfigLike {
  Response: unknown;
  State: unknown;
  Context: unknown;
  Middleware: unknown;
  Tools: unknown;
}
/**
 * Check if a type is agent-like (has `~agentTypes` phantom property).
 * This property is present on `ReactAgent` instances created with `createAgent`.
 */
type IsAgentLike<T> = T extends {
  "~agentTypes": AgentTypeConfigLike;
} ? true : false;
/**
 * Extract the AgentTypeConfig from an agent-like type.
 *
 * @example
 * ```ts
 * const agent = createAgent({ ... });
 * type Config = ExtractAgentConfig<typeof agent>;
 * // Config is the AgentTypeConfig with Response, State, Context, Middleware, Tools
 * ```
 */
type ExtractAgentConfig<T> = T extends {
  "~agentTypes": infer Config;
} ? Config extends AgentTypeConfigLike ? Config : never : never;
/**
 * Helper type to infer schema input type, supporting both Zod v3 and v4.
 * - Zod v4 uses `_zod.input` property
 * - Zod v3 uses `_input` property
 */
type InferSchemaInput<S> = S extends {
  _zod: {
    input: infer Args;
  };
} ? Args : S extends {
  _input: infer Args;
} ? Args : never;
/**
 * Helper type to extract the input type from a DynamicStructuredTool's _call method.
 * This is more reliable than trying to infer from the schema directly because
 * DynamicStructuredTool has the input type baked into its _call signature.
 */
type InferToolInput<T> = T extends {
  _call: (arg: infer Args, ...rest: any[]) => any;
} ? Args : T extends {
  schema: infer S;
} ? InferSchemaInput<S> : never;
/**
 * Extract a tool call type from a single tool.
 * Works with tools created via `tool()` from `@langchain/core/tools`.
 *
 * This extracts the literal name type from DynamicStructuredTool's NameT parameter
 * and the args type from the _call method or schema's input property.
 */
type ToolCallFromAgentTool<T> = T extends {
  name: infer N;
} ? N extends string ? InferToolInput<T> extends infer Args ? Args extends never ? never : Args extends Record<string, any> ? {
  name: N;
  args: Args;
  id?: string;
  type?: "tool_call";
} : never : never : never : never;
/**
 * Extract tool calls type from an agent's tools.
 * Converts the tools array to a discriminated union of tool calls.
 *
 * This handles both tuple types (e.g., `readonly [Tool1, Tool2]`) and
 * array-of-union types (e.g., `readonly (Tool1 | Tool2)[]`) which is how
 * `createAgent` captures tool types.
 *
 * @example
 * ```ts
 * const agent = createAgent({ tools: [getWeather, search], ... });
 * type ToolCalls = InferAgentToolCalls<typeof agent>;
 * // ToolCalls is:
 * // | { name: "get_weather"; args: { location: string }; id?: string }
 * // | { name: "search"; args: { query: string }; id?: string }
 * ```
 */
type InferAgentToolCalls<T> = ExtractAgentConfig<T>["Tools"] extends readonly (infer Tool)[] ? ToolCallFromAgentTool<Tool> extends never ? DefaultToolCall : ToolCallFromAgentTool<Tool> : DefaultToolCall;
/**
 * Extract the tool call type parameter from an AIMessage in a message union.
 * Returns `never` if the message is not an AIMessage or uses DefaultToolCall.
 *
 * The key distinction: custom tool calls have literal `name` types (e.g., "get_weather"),
 * while DefaultToolCall has `name: string`. We check if `string extends TC["name"]` -
 * if true, it's DefaultToolCall; if false, it's a custom type with literal names.
 */
type ExtractToolCallFromMessageUnion<M> = M extends AIMessage<infer TC> ? TC extends {
  name: infer N;
} ? string extends N ? never : TC : never : never;
/**
 * Extract the tool call type from a StateType's messages property.
 * This is the primary way to specify tool call types when using useStream.
 *
 * @example
 * ```ts
 * // Define state with typed messages
 * type MyToolCalls =
 *   | { name: "get_weather"; args: { location: string }; id?: string }
 *   | { name: "search"; args: { query: string }; id?: string };
 *
 * interface MyState {
 *   messages: Message<MyToolCalls>[];
 * }
 *
 * // ExtractToolCallsFromState<MyState> = MyToolCalls
 * ```
 */
type ExtractToolCallsFromState<StateType extends Record<string, unknown>> = StateType extends {
  messages: infer Messages;
} ? Messages extends readonly (infer M)[] ? ExtractToolCallFromMessageUnion<M> : Messages extends (infer M)[] ? ExtractToolCallFromMessageUnion<M> : never : never;
type MessageMetadata<StateType extends Record<string, unknown>> = {
  /**
   * The ID of the message used.
   */
  messageId: string;
  /**
   * The first thread state the message was seen in.
   */
  firstSeenState: ThreadState<StateType> | undefined;
  /**
   * The branch of the message.
   */
  branch: string | undefined;
  /**
   * The list of branches this message is part of.
   * This is useful for displaying branching controls.
   */
  branchOptions: string[] | undefined;
  /**
   * Metadata sent alongside the message during run streaming.
   * @remarks This metadata only exists temporarily in browser memory during streaming and is not persisted after completion.
   */
  streamMetadata: Record<string, unknown> | undefined;
};
type GetUpdateType<Bag extends BagTemplate, StateType extends Record<string, unknown>> = Bag extends {
  UpdateType: unknown;
} ? Bag["UpdateType"] : Partial<StateType>;
type GetConfigurableType<Bag extends BagTemplate> = Bag extends {
  ConfigurableType: Record<string, unknown>;
} ? Bag["ConfigurableType"] : Record<string, unknown>;
type GetInterruptType<Bag extends BagTemplate> = Bag extends {
  InterruptType: unknown;
} ? Bag["InterruptType"] : unknown;
type GetCustomEventType<Bag extends BagTemplate> = Bag extends {
  CustomEventType: unknown;
} ? Bag["CustomEventType"] : unknown;
/**
 * Extract the tool call type from a StateType's messages property.
 * This is the canonical way to get typed tool calls in useStream.
 *
 * Tool call types are now extracted from the messages property of StateType,
 * rather than being specified separately in the Bag.
 *
 * @example
 * ```ts
 * // Define state with typed messages
 * type MyToolCalls =
 *   | { name: "get_weather"; args: { location: string }; id?: string }
 *   | { name: "search"; args: { query: string }; id?: string };
 *
 * interface MyState {
 *   messages: Message<MyToolCalls>[];
 * }
 *
 * // GetToolCallsType<MyState> = MyToolCalls
 * ```
 */
type GetToolCallsType<StateType extends Record<string, unknown>> = ExtractToolCallsFromState<StateType> extends never ? DefaultToolCall : ExtractToolCallsFromState<StateType>;
interface RunCallbackMeta {
  run_id: string;
  thread_id: string;
}
interface UseStreamThread<StateType extends Record<string, unknown>> {
  data: ThreadState<StateType>[] | null | undefined;
  error: unknown;
  isLoading: boolean;
  mutate: (mutateId?: string) => Promise<ThreadState<StateType>[] | null | undefined>;
}
interface UseStreamOptions<StateType extends Record<string, unknown> = Record<string, unknown>, Bag extends BagTemplate = BagTemplate> {
  /**
   * The ID of the assistant to use.
   */
  assistantId: string;
  /**
   * Client used to send requests.
   */
  client?: Client;
  /**
   * The URL of the API to use.
   */
  apiUrl?: ClientConfig["apiUrl"];
  /**
   * The API key to use.
   */
  apiKey?: ClientConfig["apiKey"];
  /**
   * Custom call options, such as custom fetch implementation.
   */
  callerOptions?: ClientConfig["callerOptions"];
  /**
   * Default headers to send with requests.
   */
  defaultHeaders?: ClientConfig["defaultHeaders"];
  /**
   * Specify the key within the state that contains messages.
   * Defaults to "messages".
   *
   * @default "messages"
   */
  messagesKey?: string;
  /**
   * Callback that is called when an error occurs.
   */
  onError?: (error: unknown, run: RunCallbackMeta | undefined) => void;
  /**
   * Callback that is called when the stream is finished.
   */
  onFinish?: (state: ThreadState<StateType>, run: RunCallbackMeta | undefined) => void;
  /**
   * Callback that is called when a new stream is created.
   */
  onCreated?: (run: RunCallbackMeta) => void;
  /**
   * Callback that is called when an update event is received.
   */
  onUpdateEvent?: (data: UpdatesStreamEvent<GetUpdateType<Bag, StateType>>["data"], options: {
    namespace: string[] | undefined;
    mutate: (update: Partial<StateType> | ((prev: StateType) => Partial<StateType>)) => void;
  }) => void;
  /**
   * Callback that is called when a custom event is received.
   */
  onCustomEvent?: (data: CustomStreamEvent<GetCustomEventType<Bag>>["data"], options: {
    namespace: string[] | undefined;
    mutate: (update: Partial<StateType> | ((prev: StateType) => Partial<StateType>)) => void;
  }) => void;
  /**
   * Callback that is called when a metadata event is received.
   */
  onMetadataEvent?: (data: MetadataStreamEvent["data"]) => void;
  /**
   * Callback that is called when a LangChain event is received.
   * @see https://langchain-ai.github.io/langgraph/cloud/how-tos/stream_events/#stream-graph-in-events-mode for more details.
   */
  onLangChainEvent?: (data: EventsStreamEvent["data"]) => void;
  /**
   * Callback that is called when a debug event is received.
   * @internal This API is experimental and subject to change.
   */
  onDebugEvent?: (data: DebugStreamEvent["data"], options: {
    namespace: string[] | undefined;
  }) => void;
  /**
   * Callback that is called when a checkpoints event is received.
   */
  onCheckpointEvent?: (data: CheckpointsStreamEvent<StateType>["data"], options: {
    namespace: string[] | undefined;
  }) => void;
  /**
   * Callback that is called when a tasks event is received.
   */
  onTaskEvent?: (data: TasksStreamEvent<StateType, GetUpdateType<Bag, StateType>>["data"], options: {
    namespace: string[] | undefined;
  }) => void;
  /**
   * Callback that is called when the stream is stopped by the user.
   * Provides a mutate function to update the stream state immediately
   * without requiring a server roundtrip.
   *
   * @example
   * ```typescript
   * onStop: ({ mutate }) => {
   *   mutate((prev) => ({
   *     ...prev,
   *     ui: prev.ui?.map(component =>
   *       component.props.isLoading
   *         ? { ...component, props: { ...component.props, stopped: true, isLoading: false }}
   *         : component
   *     )
   *   }));
   * }
   * ```
   */
  onStop?: (options: {
    mutate: (update: Partial<StateType> | ((prev: StateType) => Partial<StateType>)) => void;
  }) => void;
  /**
   * The ID of the thread to fetch history and current values from.
   */
  threadId?: string | null;
  /**
   * Callback that is called when the thread ID is updated (ie when a new thread is created).
   */
  onThreadId?: (threadId: string) => void;
  /** Will reconnect the stream on mount */
  reconnectOnMount?: boolean | (() => RunMetadataStorage);
  /**
   * Initial values to display immediately when loading a thread.
   * Useful for displaying cached thread data while official history loads.
   * These values will be replaced when official thread data is fetched.
   *
   * Note: UI components from initialValues will render immediately if they're
   * predefined in LoadExternalComponent's components prop, providing instant
   * cached UI display without server fetches.
   */
  initialValues?: StateType | null;
  /**
   * Whether to fetch the history of the thread.
   * If true, the history will be fetched from the server. Defaults to 10 entries.
   * If false, only the last state will be fetched from the server.
   * @default true
   */
  fetchStateHistory?: boolean | {
    limit: number;
  };
  /**
   * Manage the thread state externally.
   */
  thread?: UseStreamThread<StateType>;
  /**
   * Throttle the stream.
   * If a number is provided, the stream will be throttled to the given number of milliseconds.
   * If `true`, updates are batched in a single macrotask.
   * If `false`, updates are not throttled or batched.
   * @default true
   */
  throttle?: number | boolean;
}
interface RunMetadataStorage {
  getItem(key: `lg:stream:${string}`): string | null;
  setItem(key: `lg:stream:${string}`, value: string): void;
  removeItem(key: `lg:stream:${string}`): void;
}
type ConfigWithConfigurable<ConfigurableType extends Record<string, unknown>> = Config & {
  configurable?: ConfigurableType;
};
interface SubmitOptions<StateType extends Record<string, unknown> = Record<string, unknown>, ContextType extends Record<string, unknown> = Record<string, unknown>> {
  config?: ConfigWithConfigurable<ContextType>;
  context?: ContextType;
  checkpoint?: Omit<Checkpoint, "thread_id"> | null;
  command?: Command;
  interruptBefore?: "*" | string[];
  interruptAfter?: "*" | string[];
  metadata?: Metadata;
  multitaskStrategy?: MultitaskStrategy;
  onCompletion?: OnCompletionBehavior;
  onDisconnect?: DisconnectMode;
  feedbackKeys?: string[];
  streamMode?: Array<StreamMode>;
  runId?: string;
  optimisticValues?: Partial<StateType> | ((prev: StateType) => Partial<StateType>);
  /**
   * Whether or not to stream the nodes of any subgraphs called
   * by the assistant.
   * @default false
   */
  streamSubgraphs?: boolean;
  /**
   * Mark the stream as resumable. All events emitted during the run will be temporarily persisted
   * in order to be re-emitted if the stream is re-joined.
   * @default false
   */
  streamResumable?: boolean;
  /**
   * Whether to checkpoint during the run (or only at the end/interruption).
   * - `"async"`: Save checkpoint asynchronously while the next step executes (default).
   * - `"sync"`: Save checkpoint synchronously before the next step starts.
   * - `"exit"`: Save checkpoint only when the graph exits.
   * @default "async"
   */
  durability?: Durability;
  /**
   * The ID to use when creating a new thread. When provided, this ID will be used
   * for thread creation when threadId is `null` or `undefined`.
   * This enables optimistic UI updates where you know the thread ID
   * before the thread is actually created.
   */
  threadId?: string;
}
/**
 * Transport used to stream the thread.
 * Only applicable for custom endpoints using `toLangGraphEventStream` or `toLangGraphEventStreamResponse`.
 */
interface UseStreamTransport<StateType extends Record<string, unknown> = Record<string, unknown>, Bag extends BagTemplate = BagTemplate> {
  stream: (payload: {
    input: GetUpdateType<Bag, StateType> | null | undefined;
    context: GetConfigurableType<Bag> | undefined;
    command: Command | undefined;
    config: ConfigWithConfigurable<GetConfigurableType<Bag>> | undefined;
    signal: AbortSignal;
  }) => Promise<AsyncGenerator<{
    id?: string;
    event: string;
    data: unknown;
  }>>;
}
type UseStreamCustomOptions<StateType extends Record<string, unknown> = Record<string, unknown>, Bag extends BagTemplate = BagTemplate> = Pick<UseStreamOptions<StateType, Bag>, "messagesKey" | "threadId" | "onThreadId" | "onError" | "onCreated" | "onUpdateEvent" | "onCustomEvent" | "onMetadataEvent" | "onLangChainEvent" | "onDebugEvent" | "onCheckpointEvent" | "onTaskEvent" | "onStop" | "initialValues" | "throttle"> & {
  transport: UseStreamTransport<StateType, Bag>;
};
type CustomSubmitOptions<StateType extends Record<string, unknown> = Record<string, unknown>, ConfigurableType extends Record<string, unknown> = Record<string, unknown>> = Pick<SubmitOptions<StateType, ConfigurableType>, "optimisticValues" | "context" | "command" | "config">;
//#endregion
export { AgentTypeConfigLike, CustomSubmitOptions, ExtractAgentConfig, GetConfigurableType, GetInterruptType, GetToolCallsType, GetUpdateType, InferAgentToolCalls, IsAgentLike, MessageMetadata, RunCallbackMeta, SubmitOptions, UseStreamCustomOptions, UseStreamOptions, UseStreamThread, UseStreamTransport };
//# sourceMappingURL=types.d.ts.map