/**
 * Public types for the Chat tool.
 *
 * Shapes here are the contract between transport adapters, the reducer,
 * the hooks layer and the components. Keep serializable (no Date, no Map).
 */

export type ChatRole = 'user' | 'assistant' | 'system';

export interface ChatToolCall {
  id: string;
  name: string;
  input: unknown;
  output?: unknown;
  /** Live buffer accumulated during streaming. Replaced by `output` on completion. */
  streamingText?: string;
  status: 'running' | 'success' | 'error' | 'cancelled';
  startedAt: number;
  endedAt?: number;
  /** Optional grouping label for parallel/fan-out execution. */
  sourceHostname?: string;
}

export interface ChatAttachment {
  id: string;
  type: 'image' | 'file' | 'audio' | 'video';
  url: string;
  thumbnailUrl?: string;
  name?: string;
  mimeType?: string;
  sizeBytes?: number;
  status?: 'uploading' | 'ready' | 'error';
  /** 0..1 while uploading. */
  progress?: number;
  /** Extracted text from images (OCR). */
  ocrText?: string;
}

export interface ChatSource {
  title: string;
  url: string;
  snippet?: string;
  chunkIndex?: number;
}

export interface ChatPersona {
  /** Display name. */
  name?: string;
  /** Avatar image URL. */
  avatarUrl?: string;
  /** Initials fallback when no avatar. Auto-derived from `name` if absent. */
  initials?: string;
  /** Tooltip / aria description. */
  description?: string;
}

export interface ChatMessage {
  id: string;
  role: ChatRole;
  content: string;
  /** epoch ms — serializable. */
  createdAt: number;
  isStreaming?: boolean;
  isError?: boolean;
  /** Bumps on edit so memo keys invalidate. */
  version?: number;
  /** Per-message persona override (multi-user / multi-bot). Falls back to
   *  `ChatConfig.user` / `ChatConfig.assistant` when absent. */
  sender?: ChatPersona;
  /** Simple status indicator above message ("Searching knowledge base..."). */
  toolActivity?: string;
  toolCalls?: ChatToolCall[];
  attachments?: ChatAttachment[];
  sources?: ChatSource[];
  tokensIn?: number;
  tokensOut?: number;
}

export type ChatDisplayMode = 'closed' | 'embedded' | 'floating' | 'sidebar' | 'fullscreen';

export interface ChatUserContext extends ChatPersona {
  email?: string;
  language?: string;
  role?: string;
  /** Free-form custom data forwarded to transport metadata. */
  custom?: Record<string, unknown>;
}

export interface ChatAssistantContext extends ChatPersona {
  /** Model identifier (gpt-4o, claude-opus, …). */
  model?: string;
}

export interface ChatPrefs {
  /** Submit hotkey for the composer. */
  submitOn?: 'enter' | 'cmd+enter';
  /** UI density. */
  density?: 'comfortable' | 'compact';
  /** Locale forwarded to transport metadata. */
  locale?: string;
  /** Show timestamps on each bubble. */
  showTimestamps?: boolean;
}

export interface ChatConfig {
  /** Window title / aria-label. */
  title?: string;
  /** Composer placeholder. */
  placeholder?: string;
  /** Empty-state greeting. */
  greeting?: string;
  /** Empty-state description. */
  description?: string;
  /** Suggested prompts shown on empty conversation. */
  suggestions?: Array<{ label: string; prompt: string }>;
  /** Locale forwarded to the transport via stream metadata. */
  locale?: string;
  /** Project / chat slug forwarded to the transport. */
  slug?: string;
  /** UI density. Use `prefs.density` for the same effect; this stays for
   *  backwards compatibility. */
  density?: 'comfortable' | 'compact';
  /** Identity of the human author. Renders avatar / name on user bubbles
   *  and gets stamped on outgoing messages as `message.sender`. */
  user?: ChatUserContext;
  /** Identity of the assistant. Renders avatar / name on assistant bubbles. */
  assistant?: ChatAssistantContext;
  /** UI preferences. */
  prefs?: ChatPrefs;
  /** Visual labels (i18n is the host's job). */
  labels?: Partial<ChatLabels>;
}

export interface ChatLabels {
  send: string;
  cancel: string;
  copy: string;
  regenerate: string;
  edit: string;
  delete: string;
  retry: string;
  newChat: string;
  loadMore: string;
  jumpToLatest: string;
  attach: string;
  voice: string;
  errorGeneric: string;
  cancelledSuffix: string;
}

export const DEFAULT_LABELS: ChatLabels = {
  send: 'Send',
  cancel: 'Stop',
  copy: 'Copy',
  regenerate: 'Regenerate',
  edit: 'Edit',
  delete: 'Delete',
  retry: 'Retry',
  newChat: 'New chat',
  loadMore: 'Load more',
  jumpToLatest: 'Jump to latest',
  attach: 'Attach files',
  voice: 'Voice input',
  errorGeneric: 'Something went wrong',
  cancelledSuffix: '[cancelled]',
};

// ---- Transport ------------------------------------------------------------

export interface CreateSessionOptions {
  metadata?: Record<string, unknown>;
}

export interface SessionInfo {
  sessionId: string;
  /** If the server resumed an existing session, it may return prior messages. */
  messages?: ChatMessage[];
  hasMore?: boolean;
  cursor?: string | null;
  resumed?: boolean;
}

export interface HistoryPage {
  messages: ChatMessage[];
  hasMore: boolean;
  nextCursor: string | null;
}

export interface StreamOptions {
  signal: AbortSignal;
  attachments?: ChatAttachment[];
  metadata?: Record<string, unknown>;
}

export interface SendOptions {
  signal?: AbortSignal;
  attachments?: ChatAttachment[];
  metadata?: Record<string, unknown>;
}

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';
      tokensIn?: number;
      tokensOut?: number;
      sources?: ChatSource[];
    }
  | { type: 'error'; code: string; message: string };

export interface ChatTransport {
  createSession(opts?: CreateSessionOptions): Promise<SessionInfo>;
  loadHistory(sessionId: string, cursor?: string | null, limit?: number): Promise<HistoryPage>;
  stream(
    sessionId: string,
    content: string,
    options: StreamOptions,
  ): AsyncGenerator<ChatStreamEvent, void, void>;
  send(sessionId: string, content: string, options?: SendOptions): Promise<ChatMessage>;
  closeSession(sessionId: string): Promise<void>;
}
