/**
 * Versioned WebSocket frame envelopes shared by citations (#98) and status (#96).
 * Shape: { v: 1, type: string, data: unknown }
 */

export type VersionedFrame = {
  v: number;
  type: string;
  data: unknown;
};

export type Citation = {
  index: number;
  title: string;
  url: string;
  published_at?: string | null;
};

export function parseVersionedFrame(raw: string): VersionedFrame | null {
  const trimmed = raw?.trim?.() ?? '';
  if (!trimmed.startsWith('{')) return null;
  try {
    const parsed = JSON.parse(trimmed);
    if (
      parsed &&
      typeof parsed === 'object' &&
      typeof parsed.v === 'number' &&
      typeof parsed.type === 'string'
    ) {
      return {
        v: parsed.v,
        type: parsed.type,
        data: parsed.data,
      };
    }
  } catch {
    // not JSON — fall through to sentinel / stream text handling
  }
  return null;
}

export type StatusFrameData = {
  stage: string;
  label: string;
  detail?: string | null;
};

export type ActivityHistoryEntry = {
  stage: string;
  label: string;
  startedAt: number;
  finishedAt: number;
};

export function parseStatusData(data: unknown): StatusFrameData | null {
  if (!data || typeof data !== 'object') return null;
  const obj = data as Record<string, unknown>;
  if (typeof obj.stage !== 'string' || !obj.stage) return null;
  // label may be empty (e.g. stage "writing" — tokens take over)
  if (typeof obj.label !== 'string') return null;
  const detail = typeof obj.detail === 'string' ? obj.detail : null;
  return { stage: obj.stage, label: obj.label, detail };
}

export function parseCitationsData(data: unknown): Citation[] {
  if (!Array.isArray(data)) return [];
  return data
    .filter(
      (item): item is Record<string, unknown> =>
        !!item && typeof item === 'object' && typeof (item as { index?: unknown }).index === 'number',
    )
    .map((item) => ({
      index: item.index as number,
      title: typeof item.title === 'string' ? item.title : `Source ${item.index}`,
      url: typeof item.url === 'string' ? item.url : '',
      published_at:
        typeof item.published_at === 'string' ? item.published_at : null,
    }));
}
