/**
 * SSE stream events — the canonical wire shape consumed by the reducer.
 *
 * Backend-specific shapes (e.g. pydantic-AI's `text_delta`) get normalized
 * into this union by `mapPydanticAIEvent` and friends.
 */

import type { ChatMessage } from './message';

/**
 * Per-run metrics block. Emitted once per assistant turn, carried into the
 * message either by a non-terminal `message_metrics` event mid-run or via
 * the terminal `message_end` event's generic `patch` slot.
 *
 * All fields optional — the backend omits anything it could not measure.
 */
export interface ChatMessageMetrics {
  inputTokens?: number;
  outputTokens?: number;
  totalTokens?: number;
  cacheReadTokens?: number;
  cacheWriteTokens?: number;
  processingTimeMs?: number;
  firstTokenMs?: number;
  turns?: number;
  toolCallCount?: number;
  toolNames?: string[];
  /** Model the request was sent with (may be an alias). */
  model?: string;
  /** Concrete model the alias resolved to. */
  resolvedModel?: string;
  /** Short summary of the model's thinking, if available. */
  thinkingSummary?: string;
}

export type ChatStreamEvent =
  | { type: 'message_start'; messageId: string; sessionId: string }
  | { type: 'resume_start' }
  | { type: 'chunk'; delta: string }
  | { type: 'tool_activity'; tool: string; status: string }
  | {
      type: 'tool_call_start';
      toolId: string;
      name: string;
      input: unknown;
      sourceHostname?: string;
    }
  | { type: 'tool_call_delta'; toolId: string; delta: string }
  | {
      type: 'tool_call_end';
      toolId: string;
      output: unknown;
      status: 'success' | 'error';
    }
  | {
      type: 'message_end';
      /**
       * Generic finalize patch merged into the message on done — the ONE
       * slot every finalize feature rides (tokens, sources, blocks, OCR
       * text, attachments-finalized, local-preview metadata). `content`
       * here REPLACES the accumulated bubble body (the append-only gap).
       * The reducer applies it as a `Partial<ChatMessage>` merge — the same
       * mechanism `message_metrics`/`resolved_model` already use. Absent →
       * `message_end` is a pure terminal no-op (just clears `isStreaming`).
       */
      patch?: Partial<ChatMessage>;
    }
  | {
      /**
       * Per-turn metrics for the assistant message currently streaming.
       * Non-terminal — arrives near the end of the run but does not
       * close the stream (`message_end` / `error` do that).
       */
      type: 'message_metrics';
      metrics: ChatMessageMetrics;
    }
  | {
      /**
       * One-shot model-alias resolution (e.g. `@code` → `glm-5.1`).
       * Non-terminal — lets the UI update the model chip mid-run.
       */
      type: 'resolved_model';
      /** Alias the user/request originally specified. */
      originalAlias: string;
      /** Concrete model id the alias resolved to. */
      resolvedModel: string;
      /** True when routing upgraded the model (e.g. for a larger context). */
      upgraded?: boolean;
      /** Human-readable reason the router picked this model. */
      routingReason?: string;
    }
  | { type: 'error'; code: string; message: string };
