/**
 * Runtime contracts — the input/output types for `runTurn`.
 *
 * The kernel's turn loop is a pure function of these inputs. It takes
 * messages as a plain input, returns result messages, and touches no DB.
 * The implementation lives in the kernel runtime module; these types are
 * the contract the session-orchestrator (core) programs against.
 */

import type { ChatMessage } from "./conversation.js";
import type { ToolDispatchPolicy } from "./dispatch.js";
import type { AgentEvent } from "./events.js";
import type { Logger } from "./logging.js";
import type { ProviderContract, ProviderStreamOptions, Usage } from "./provider.js";
import type { ToolContract } from "./tool.js";

/**
 * The emitter function the kernel calls to push events outward.
 * The session-orchestrator provides this, wiring it to transport + persistence.
 */
export type EventEmitter = (event: AgentEvent) => void;

/**
 * Why a turn ended. Known kernel/provider reasons are enumerated for ergonomics;
 * the trailing `(string & {})` keeps the type open for provider-specific reasons
 * passed through verbatim without losing autocomplete on the known values.
 */
export type FinishReason =
	| "stop"
	| "tool-calls"
	| "length"
	| "content-filter"
	| "max-steps"
	| "error"
	| "aborted"
	| (string & {});

/**
 * Input to `runTurn` — everything the kernel needs to execute one turn.
 * All fields are resolved by the session-orchestrator before calling;
 * the kernel never reads config or resolves providers/tools itself.
 */
export interface RunTurnInput {
	/** The resolved provider to stream from. */
	readonly provider: ProviderContract;

	/** The conversation history (including system prompt as first message). */
	readonly messages: readonly ChatMessage[];

	/** The tool set available for this turn (may be empty). */
	readonly tools: readonly ToolContract[];

	/** How to dispatch tool calls within each step. */
	readonly dispatch: ToolDispatchPolicy;

	/** The emitter the kernel calls for each outward event. */
	readonly emit: EventEmitter;

	/**
	 * Identifiers used to attribute every emitted `AgentEvent`. The kernel does
	 * not generate these — the session-orchestrator owns turn/conversation identity
	 * and passes them in, so events are traceable to their conversation.
	 */
	readonly conversationId: string;
	readonly turnId: string;

	/**
	 * Optional per-turn provider options (model, temperature, maxTokens,
	 * systemPrompt). The orchestrator resolves these; the kernel forwards them
	 * verbatim to `provider.stream` and never interprets them. A provider may
	 * also be pre-configured at construction and ignore these.
	 */
	readonly providerOpts?: ProviderStreamOptions;

	/** Cancellation signal for the entire turn. */
	readonly signal?: AbortSignal;

	/**
	 * Working directory for this turn's tool execution. The kernel does NOT
	 * interpret it — it forwards the value verbatim to each `ToolExecuteContext.cwd`
	 * so tools resolve/contain paths against it. It never enters the model prompt,
	 * so it does not affect prompt caching. When omitted, tools fall back to their
	 * own configured/default workdir.
	 */
	readonly cwd?: string;

	/**
	 * Optional logger for structured span instrumentation. The runtime opens
	 * turn/step/tool-call spans using this logger. If omitted, no spans are
	 * emitted (backward-compatible with callers that don't yet pass a logger).
	 */
	readonly logger?: Logger;

	/**
	 * Optional monotonic-ish clock (milliseconds) for emitting wall-clock timing
	 * on outward events: per-step `step-complete` (ttft/decode/genTotal), tool
	 * execution `durationMs` on `tool-result`, and turn `durationMs` on `done`.
	 * Injected (not ambient) so the runtime stays pure and deterministic in tests.
	 * If omitted, the runtime emits no such timing (the optional fields stay
	 * absent) — backward-compatible with callers that don't provide a clock.
	 */
	readonly now?: () => number;

	/**
	 * Optional. Called by the runtime at the tool-result boundary — after a
	 * step whose tool calls have all executed, before the next step begins —
	 * to drain messages to inject alongside the tool results. Whatever it
	 * returns is appended as user-role messages to the next step's input, so
	 * a caller can inject mid-turn guidance the model sees with the tool
	 * results. When omitted or returning an empty array, no injection happens
	 * (the runtime is unchanged).
	 *
	 * Injected (not ambient) so the kernel stays pure: it owns no queue and
	 * names no feature — it just calls the callback and appends what it gets.
	 * Only invoked when a step PRODUCED tool calls (the tool-result boundary);
	 * a step that ends without tool calls does not drain (the caller decides
	 * what to do with any pending messages after the turn ends).
	 */
	readonly drainSteering?: () => readonly ChatMessage[];
}

/**
 * The result of a completed turn. The session-orchestrator uses this to
 * persist the new messages and report usage.
 */
export interface RunTurnResult {
	/** The assistant messages produced by this turn (appended to history). */
	readonly messages: readonly ChatMessage[];

	/** Aggregated token usage across all steps in the turn. */
	readonly usage: Usage;

	/** Why the turn ended. */
	readonly finishReason: FinishReason;
}
