/**
 * The Loyaly AI conversation model.
 *
 * ── Why a message is PARTS, not a string ──────────────────────────────────
 * An assistant that answers business questions will not answer only in prose.
 * "Compare stores" wants a table, "analyze sales" wants a chart, an uploaded
 * invoice wants a file preview. Modelling a message as `text: string` forces
 * every one of those to be smuggled through markdown or bolted on later as a
 * second field, and both roads end in a renderer that switches on the shape of
 * a string.
 *
 * So a message carries an ordered list of PARTS, each independently typed and
 * independently rendered (see components/MessageContent). Today only `text`
 * has a renderer. Adding charts is a new variant here plus a case there —
 * nothing about the transport, the provider or the message list changes.
 */

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

/** Prose. Rendered as markdown: lists, tables, code blocks all come free. */
export interface TextPart {
  type: 'text';
  text: string;
}

/**
 * A chart the assistant chose to draw. Deliberately references a dataset by id
 * rather than embedding points: an answer that ships 90 days of raw series
 * inside a chat message cannot be cached, diffed or re-rendered at a new size.
 */
export interface ChartPart {
  type: 'chart';
  chart: 'line' | 'bar' | 'area';
  title: string;
  datasetId: string;
}

/** A result set the merchant can sort and page through. */
export interface TablePart {
  type: 'table';
  title: string;
  columns: {key: string; header: string}[];
  rows: Record<string, string | number>[];
}

/** An uploaded or generated artefact. */
export interface FilePart {
  type: 'file';
  name: string;
  mimeType: string;
  sizeBytes: number;
  url: string;
}

export interface ImagePart {
  type: 'image';
  url: string;
  alt: string;
}

export interface ReportKpi {
  id: string;
  title: string;
  value: string;
  trend?: string;
  trendDirection?: 'up' | 'down' | 'neutral';
  iconName?: string;
  sparkline?: number[];
}

export interface ReportInsight {
  id: string;
  type: 'opportunity' | 'risk' | 'growth' | 'best_performer';
  title: string;
  explanation: string;
  confidence?: number;
  recommendedAction?: string;
}

export interface ReportAction {
  id: string;
  label: string;
  iconName?: string;
  actionType: string;
}

export interface ChartPartSpec {
  title: string;
  subtitle?: string;
  chartType: 'line' | 'bar' | 'area';
  data: Record<string, any>[];
  xKey: string;
  series: {key: string; label: string}[];
}

export interface TablePartSpec {
  title: string;
  columns: {key: string; header: string; align?: 'start' | 'center' | 'end'}[];
  rows: Record<string, any>[];
}

export interface ReportPart {
  type: 'report';
  title?: string;
  summary?: string;
  kpis?: ReportKpi[];
  charts?: ChartPartSpec[];
  tables?: TablePartSpec[];
  insights?: ReportInsight[];
  actions?: ReportAction[];
}

export type MessagePart =
  | TextPart
  | ChartPart
  | TablePart
  | FilePart
  | ImagePart
  | ReportPart;

export interface ChatMessage {
  id: string;
  role: MessageRole;
  parts: MessagePart[];
  /** ISO-8601, stamped on send — never during render. */
  at: string;
  /**
   * True while tokens are still arriving. Drives the streaming cursor and
   * tells Markdown to tolerate half-finished syntax.
   */
  isStreaming?: boolean;
}

export interface Conversation {
  id: string;
  /** Derived from the first user message; "New chat" until there is one. */
  title: string;
  messages: ChatMessage[];
  createdAt: string;
  updatedAt: string;
}

/** A conversation as the history drawer needs it — no message bodies. */
export interface ConversationSummary {
  id: string;
  title: string;
  updatedAt: string;
  messageCount: number;
}

/** One suggestion chip on the empty state. */
export interface Suggestion {
  id: string;
  label: string;
  /** What is actually sent — chips are short, prompts should not be. */
  prompt: string;
}
