import type { JSONSchema7 } from 'json-schema';
import type { ZodType } from 'zod';

import type { AgentExecutionCounter } from './agent';
import type { AgentMessage } from './message';
import type { AgentEventData } from '../runtime/event';
import type { BuiltTelemetry } from '../telemetry';
import type { JSONObject, JSONValue } from '../utils/json';

export interface ToolSuspendOptions {
	/** Schema for data accepted when resuming this specific suspension. */
	resumeSchema?: ZodType | JSONSchema7;
	/** Private serializable state restored only to the tool on resume or cancellation. */
	continuation?: JSONValue;
}

export interface ToolExecutionContext {
	/** Agent run ID for the current execution. */
	runId?: string;
	/**
	 * Current persisted thread scope when the run is backed by memory.
	 * The runtime owns these IDs so tools can read/update data tied to the
	 * current agent thread without asking the model for thread identifiers.
	 * Integration tools use it for message context and the latest response target.
	 */
	persistence?: {
		threadId: string;
		resourceId: string;
		hostMetadata?: JSONObject;
	};
	/** Internal runtime event bridge for platform-managed tools. */
	emitEvent?: (event: AgentEventData) => void;
	/**
	 * The current run's abort signal. Long-running tools (e.g. ones that spawn a
	 * child agent) should forward it so cancelling the parent run also cancels
	 * the work they started.
	 */
	abortSignal?: AbortSignal;
	/** Aggregate execution counter for usage telemetry inherited from the current agent run. */
	executionCounter?: AgentExecutionCounter;
	/** Internal runtime hook used to retain cleanup ownership if abort wins the suspend race. */
	onSuspend?: (payload: unknown, options?: ToolSuspendOptions) => void | Promise<void>;
	/**
	 * Checkpointed suspend payload for a resumed interruptible tool call,
	 * restored from persistence. Only set when the tool is being resumed.
	 */
	suspendPayload?: unknown;
	/** Private continuation restored from persistence for a resumed tool call. */
	continuation?: JSONValue;
	/** Resume schema restored from persistence for a resumed tool call. */
	resumeSchema?: ToolSuspendOptions['resumeSchema'];
}

export interface ToolContext {
	/** AI SDK tool call ID for the current local tool execution. */
	toolCallId?: string;
	/** Exact model-facing name of the tool being executed. */
	toolName?: string;
	/** Agent run ID and persistence scope for the current execution. */
	runId?: string;
	/** Current persisted thread scope when the run is backed by memory. */
	persistence?: ToolExecutionContext['persistence'];
	/** Telemetry config from the parent agent. */
	parentTelemetry?: BuiltTelemetry;
	/** Internal runtime event bridge for platform-managed tools. */
	emitEvent?: ToolExecutionContext['emitEvent'];
	/** The current run's abort signal, for tools that start cancellable work. */
	abortSignal?: ToolExecutionContext['abortSignal'];
	/** Aggregate execution counter for usage telemetry inherited from the current agent run. */
	executionCounter?: ToolExecutionContext['executionCounter'];
}

export interface InterruptibleToolContext<S = unknown, R = unknown> {
	/**
	 * Suspend execution and send a payload to the consumer.
	 * Must be used with `return await` — the branded return type signals
	 * the execution engine to halt. Code after `return await ctx.suspend()` is unreachable.
	 */
	suspend: (payload: S, options?: ToolSuspendOptions) => Promise<never>;
	/** Data from the consumer after resume. Undefined on first invocation or when cancelled. */
	resumeData: R | undefined;
	/** Set when the resume was a cancellation and the tool opted in via `.handleCancellation()`. */
	cancellation?: { message: string };
	/** AI SDK tool call ID for the current local tool execution. */
	toolCallId?: string;
	/** Exact model-facing name of the tool being executed. */
	toolName?: string;
	/** Agent run ID for the current execution. */
	runId?: string;
	/** Current persisted thread scope when the run is backed by memory. */
	persistence?: ToolExecutionContext['persistence'];
	/** Telemetry config from the parent agent. */
	parentTelemetry?: BuiltTelemetry;
	/** Internal runtime event bridge for platform-managed tools. */
	emitEvent?: ToolExecutionContext['emitEvent'];
	/** The current run's abort signal, for tools that start cancellable work. */
	abortSignal?: ToolExecutionContext['abortSignal'];
	/** Aggregate execution counter for usage telemetry inherited from the current agent run. */
	executionCounter?: ToolExecutionContext['executionCounter'];
	/** The payload this tool passed to `suspend()` when it suspended, restored from the checkpoint. Only set when the tool is being resumed. */
	suspendPayload?: S;
	/** Private continuation this tool passed to `suspend()`, restored from the checkpoint. */
	continuation?: JSONValue;
	/** Resume schema persisted for this suspension. */
	resumeSchema?: ToolSuspendOptions['resumeSchema'];
}

export interface ToolCancellationContext extends ToolContext {
	/** User steering message that cancelled the suspended tool call. */
	cancellation: { message: string };
	/** Payload originally persisted when the tool suspended. */
	suspendPayload?: unknown;
	/** Private continuation originally persisted when the tool suspended. */
	continuation?: JSONValue;
	/** Resume schema persisted for the suspension being cancelled. */
	resumeSchema?: ToolSuspendOptions['resumeSchema'];
}

export interface BuiltTool {
	readonly name: string;
	readonly description: string;
	/**
	 * Behavioural directive paired with the tool, injected into the agent's
	 * system prompt under a `<built_in_rules>` block when the tool is added.
	 * Use for guidance the LLM needs to *decide whether to call* the tool —
	 * tool descriptions answer "what does this do?" but are weighted lower
	 * than system instructions for usage decisions.
	 */
	readonly systemInstruction?: string;
	readonly suspendSchema?: ZodType | JSONSchema7;
	readonly resumeSchema?: ZodType | JSONSchema7;
	readonly approval?: {
		readonly required: boolean;
		readonly conditional?: boolean;
	};
	/** When `true`, the handler is called on cancellation with `ctx.cancellation` set instead of being bypassed. */
	readonly handleCancellation?: boolean;
	/** Run cleanup before the runtime auto-cancels a suspended tool call. */
	readonly onCancellation?: (input: unknown, ctx: ToolCancellationContext) => Promise<void>;
	readonly toMessage?: (
		output: unknown,
	) => AgentMessage | undefined | Promise<AgentMessage | undefined>;
	/**
	 * Transform the handler output before sending it to the LLM as a tool result.
	 * The raw output is stored in history; only the transformed version goes to the model.
	 */
	readonly toModelOutput?: (output: unknown) => unknown;
	readonly handler?: (
		input: unknown,
		ctx: ToolContext | InterruptibleToolContext,
	) => Promise<unknown>;
	/**
	 * Input schema — either a Zod schema (SDK-defined tools) or a raw JSON Schema object
	 * (MCP tools). Use `isZodSchema()` to distinguish between the two at runtime.
	 */
	readonly inputSchema?: ZodType | JSONSchema7;
	readonly outputSchema?: ZodType | JSONSchema7;
	/** True for tools sourced from an MCP server. */
	readonly mcpTool?: boolean;
	/** Name of the MCP server this tool belongs to. Set when mcpTool is true. */
	readonly mcpServerName?: string;
	/** Original, unprefixed tool name reported by the MCP server. */
	readonly mcpToolName?: string;
	/**
	 * Provider-specific options forwarded to the AI SDK's `tool()` call.
	 * Keyed by provider name (e.g. `anthropic`, `openai`).
	 *
	 * Example: `{ anthropic: { eagerInputStreaming: true } }`
	 */
	readonly providerOptions?: Record<string, JSONObject>;
	/**
	 * Arbitrary platform-specific metadata attached to the tool.
	 */
	readonly metadata?: Record<string, unknown>;
	/**
	 * Whether the tool has source code that can be introspected.
	 * When `false`, the tool is treated as a platform-managed marker (e.g. an
	 * externally-resolved tool) and its source is not introspected.
	 * Defaults to `true` when absent.
	 */
	readonly editable?: boolean;
}

/**
 * A provider-defined tool (e.g. Anthropic web search, OpenAI code interpreter).
 *
 * `name` follows the AI SDK format `<provider-name>.<tool-name>`,
 * e.g. `'anthropic.web_search_20250305'` or `'openai.image_generation'`.
 * It is used as both the unique identifier and the key in the tools record
 * passed to `generateText` / `streamText`.
 */
export interface BuiltProviderTool {
	readonly name: `${string}.${string}`;
	readonly args: Record<string, unknown>;
	inputSchema?: ZodType;
}
