///|
/// Conformance testkit for Posoco (M0-T04).
///
/// Scripted, recording fakes that let tests assert behavior contracts
/// (event order, metadata fidelity, tool-outcome consistency) rather than
/// implementation details. Distinct from the minimal Mock* types used by the
/// blackbox tests: the testkit records call order, arguments, and metadata
/// snapshots, and fails loudly when a script is exhausted.
///
/// R3 M3.7: all fixtures use canonical kernel types directly. There is no
/// legacy `@types.Message` / `ModelResponse` / `ToolResult` surface any more.
///
/// The testkit is a test/support layer only — it never enters the production
/// Agent dependency direction.

// ---------------------------------------------------------------------------
// Snapshot helpers — deep-copy plumbing shared by the recording fakes.
// The kernel-level copiers (snapshot_json/tool_call/message/...) live in
// agent.mbt; only testkit-specific shapes remain here.
// ---------------------------------------------------------------------------

///|
/// Deep-copy a `@kernel.ToolDef`.
fn testkit_snapshot_tool_def(tool : @kernel.ToolDef) -> @kernel.ToolDef {
  @kernel.ToolDef(
    name=tool.name,
    description=tool.description,
    input_schema=snapshot_json(tool.input_schema),
    owner=tool.owner,
    policy=tool.policy,
    provenance=tool.provenance,
  )
}

///|
fn testkit_snapshot_completion(
  completion : @kernel.Completion,
) -> @kernel.Completion {
  let msg = completion.message
  @kernel.Completion(
    content=msg.content.map(snapshot_content),
    tool_calls=msg.tool_calls.map(snapshot_tool_call),
    reasoning=msg.reasoning,
    finish_reason=msg.finish_reason,
    usage=completion.usage,
  )
}

///|
fn testkit_snapshot_model_call_result(
  result : @kernel.ModelCallResult,
) -> @kernel.ModelCallResult {
  {
    completion: testkit_snapshot_completion(result.completion),
    processed_messages: snapshot_messages(result.processed_messages),
  }
}

///|
/// Testkit-specific event copier. Unlike `snapshot_turn_event` (agent.mbt),
/// this preserves a recorded `ToolCallResult`'s `is_error` verbatim instead
/// of recomputing it from `result.is_failure()`: the observer must snapshot
/// what it actually saw, even if some emitter disagreed with the result.
fn testkit_snapshot_event(event : @types.TurnEvent) -> @types.TurnEvent {
  match event {
    @types.TurnStarted => @types.TurnStarted
    @types.ToolCallPending(call) =>
      @types.ToolCallPending(snapshot_tool_call(call))
    @types.ToolCallResult(call~, result~, is_error~) =>
      @types.ToolCallResult(
        call=snapshot_tool_call(call),
        result=snapshot_tool_outcome(result),
        is_error~,
      )
    @types.ModelResponseReceived(message~, usage~) =>
      @types.ModelResponseReceived(message=snapshot_message(message), usage~)
    @types.SessionRedirect(from~, to~, messages_before~, messages_after~) =>
      @types.SessionRedirect(from~, to~, messages_before~, messages_after~)
    @types.TurnCompleted => @types.TurnCompleted
    @types.TurnFailed(reason) => @types.TurnFailed(reason)
    @types.ToolCallDeferred(call~, reason~) =>
      @types.ToolCallDeferred(call=snapshot_tool_call(call), reason~)
    @types.StreamChunkReceived(chunk~) => @types.StreamChunkReceived(chunk~)
    @types.StreamChunksDropped(count~) => @types.StreamChunksDropped(count~)
    @types.ConfigWarning(field~, value~, reason~) =>
      @types.ConfigWarning(field~, value~, reason~)
    @types.ConfigChanged(field~, old_value~, new_value~) =>
      @types.ConfigChanged(field~, old_value~, new_value~)
    @types.Custom(source~, label~, data~) =>
      @types.Custom(source~, label~, data=snapshot_json(data))
  }
}