/**
 * Shared types for @bytelyst/ai-ui.
 *
 * Message shape is the **subset of the Vercel AI SDK `Message` type** that
 * we commit to as stable. Per `docs/ROADMAP_2026_DECISIONS.md` §10 we adopt
 * the AI SDK hook shape but abstract the transport — additional fields
 * Vercel emits (e.g. `toolInvocations`, `experimental_attachments`) may be
 * exposed in later minor versions when the corresponding components
 * (`<ToolCallCard>`, `<AttachmentPreview>`) land.
 */

export type MessageRole = 'system' | 'user' | 'assistant' | 'tool';

export type ToolCallStatus = 'pending' | 'streaming' | 'success' | 'error';

export interface ToolInvocation {
  /** Stable id — typically the tool-call id emitted by the LLM. */
  id: string;
  /** Tool name (e.g. 'web_search', 'calculator'). */
  name: string;
  status: ToolCallStatus;
  /** Tool input — already-parsed JSON (no wire framing). */
  input?: unknown;
  /** Tool output — already-parsed JSON, or a raw string. */
  output?: unknown;
  /** Error message when status === 'error'. */
  error?: string;
  /** Total wall-clock duration in ms once the call settles. */
  durationMs?: number;
}

export interface Citation {
  /** Stable id used for deduplication and React keys. */
  id: string;
  /** 1-based index displayed in the chip. */
  index: number;
  title: string;
  url?: string;
  snippet?: string;
  /** Optional source label (e.g. 'arxiv', 'wikipedia'). */
  source?: string;
}

export interface Message {
  /** Stable id — used as React key and for deduplication. */
  id: string;
  role: MessageRole;
  /** Plain text content. Markdown rendering is opt-in via component props. */
  content: string;
  /** Wall-clock time the message was authored on the client. */
  createdAt?: Date;
  /** When true, indicates content is still streaming in (assistant only). */
  isStreaming?: boolean;
  /** Tool calls associated with this assistant turn. Added in 0.2.0. */
  toolInvocations?: ToolInvocation[];
  /** Inline citations referenced by [^n] markers in `content`. Added in 0.2.0. */
  citations?: Citation[];
}

/**
 * Wire protocol for streamed responses. The default `'text'` protocol
 * expects a plain `text/plain` body with the assistant message streamed
 * token-by-token (or chunk-by-chunk). Other protocols are reserved.
 */
export type StreamProtocol = 'text' | 'sse' | 'data';

export interface ChatTransportOptions {
  /** Endpoint that accepts POST { messages } and returns a stream. */
  endpoint?: string;
  /**
   * Wire format. Defaults to `'text'` which works with any backend
   * returning a streaming text response — including LysnrAI, JarvisJr,
   * or any FastAPI/Express endpoint that pipes an LLM stream.
   */
  streamProtocol?: StreamProtocol;
  /**
   * Optional fetch override. Useful for injecting auth headers, mock
   * transports under test (MSW), or proxying via a service-worker.
   * Defaults to the global `fetch`.
   */
  fetcher?: typeof fetch;
  /** Request headers merged on every send. */
  headers?: Record<string, string>;
}

export interface UseChatOptions extends ChatTransportOptions {
  /** Seed conversation. Re-evaluated only on mount. */
  initialMessages?: Message[];
  /** Seed the input text. */
  initialInput?: string;
  /** Called whenever a new assistant message completes (not on each token). */
  onFinish?: (message: Message) => void;
  /** Called on transport-level error. */
  onError?: (error: Error) => void;
  /**
   * Optional id generator. Defaults to a monotonic counter so two
   * concurrent hooks in the same React tree never collide. Provide one
   * if you need cryptographic ids (e.g. for persistence by id).
   */
  generateId?: () => string;
}

export interface UseChatHelpers {
  messages: Message[];
  input: string;
  setInput: (value: string) => void;
  handleInputChange: (e: { target: { value: string } }) => void;
  /** Submit current `input` as a user message and start streaming a reply. */
  handleSubmit: (e?: { preventDefault?: () => void }) => Promise<void>;
  /** Programmatic equivalent of `handleSubmit`. */
  append: (message: Omit<Message, 'id' | 'createdAt'> & Partial<Pick<Message, 'id'>>) => Promise<void>;
  /** True while a request is in flight (from POST start to stream end). */
  isLoading: boolean;
  /** Latest transport-level error, if any. Cleared on next send. */
  error: Error | undefined;
  /** Abort the in-flight request (if any) and keep partial assistant text. */
  stop: () => void;
  /** Re-run the last user turn (drops the last assistant message first). */
  reload: () => Promise<void>;
  /** Replace the entire message list. */
  setMessages: (messages: Message[]) => void;
}
