/**
 * Shared pure utility for summarizing trace-event payloads into a single
 * human-readable line.  Used by both TraceTab (full events.jsonl viewer)
 * and DelegateRunsSection (per-run mini-timeline).
 *
 * The function is intentionally minimal: it accepts the narrowest structural
 * union that satisfies both callers and returns a plain string so it can be
 * tested without any React dependency.
 */

export interface TraceEventShape {
  kind: string;
  payload: unknown;
  movement?: string;
  iteration?: number;
}

function fmtDuration(ms: number): string {
  if (!Number.isFinite(ms) || ms < 0) return '?';
  if (ms < 1000) return `${Math.round(ms)}ms`;
  if (ms < 60_000) return `${(ms / 1000).toFixed(1)}s`;
  const sec = Math.round(ms / 1000);
  return `${Math.floor(sec / 60)}m${(sec % 60).toString().padStart(2, '0')}s`;
}

/**
 * Returns a one-line summary of the event, or an empty string when there is
 * nothing useful to say.  Never throws.
 */
export function summarizeTraceEvent(event: TraceEventShape): string {
  const p = event.payload as Record<string, unknown> | null;
  if (!p) return '';
  switch (event.kind) {
    case 'tool_call': {
      const args = p.args as Record<string, unknown> | undefined;
      const target = args?.['file_path'] ?? args?.['path'] ?? args?.['url'] ?? args?.['pattern'];
      return `${String(p.tool ?? '?')}${target ? ` ${target}` : ''}`;
    }
    case 'tool_result':
      return `${String(p.tool ?? '?')} ${p.isError ? '⚠ error' : 'ok'}${p.cacheHit ? ' (cached)' : ''} ${fmtDuration(Number(p.durationMs ?? 0))}`;
    case 'llm_call_start':
      return `iter=${p.iteration ?? event.iteration ?? '?'} msgs=${p.messageCount ?? '?'}`;
    case 'llm_call_end': {
      const tokens =
        typeof p.promptTokens === 'number' && typeof p.completionTokens === 'number'
          ? ` in=${p.promptTokens} out=${p.completionTokens}`
          : '';
      const shape =
        (p.toolCalls as number) > 0
          ? ` tools=${p.toolCalls}`
          : (p.textChars as number) > 0
          ? ` text=${p.textChars}c`
          : '';
      return `${fmtDuration(Number(p.durationMs ?? 0))}${tokens}${shape}${p.hadError ? ' ⚠' : ''}`;
    }
    case 'cache_set':
      return `${String(p.tool ?? '?')} (${String(p.volatility ?? '?')})`;
    case 'cache_hit':
      return `${String(p.tool ?? '?')} from ${String(p.sourceMovement ?? '?')} (${p.ageMs ?? '?'}ms ago)`;
    case 'cache_invalidate':
    case 'memory_invalidate':
      return `${String(p.trigger ?? '')} → ${p.entriesEvicted ?? 0} entries`;
    case 'memory_update_call': {
      const counts = p.counts as Record<string, number> | null;
      if (!counts) return p.empty ? 'empty payload' : '';
      const parts: string[] = [];
      if (counts.factsAdded) parts.push(`facts +${counts.factsAdded}`);
      if (counts.factsMerged) parts.push(`facts merged ${counts.factsMerged}`);
      if (counts.decisionsAdded) parts.push(`decisions +${counts.decisionsAdded}`);
      if (counts.openQuestionsAdded) parts.push(`open_questions +${counts.openQuestionsAdded}`);
      if (counts.doNotRepeatAdded) parts.push(`do_not_repeat +${counts.doNotRepeatAdded}`);
      return parts.join(', ') || 'no changes';
    }
    case 'memory_handoff_write':
      return p.skipped
        ? `skipped: ${p.reason}`
        : `→ child #${p.subtaskIndex ?? '?'} (${p.factsCount ?? 0}f / ${p.decisionsCount ?? 0}d)`;
    case 'memory_handoff_read':
      return `from parent ${String(p.parentJobId ?? '?')}`;
    case 'memory_delta_write':
      return p.skipped
        ? `skipped: ${p.reason}`
        : `${p.childStatus} ${p.partial ? '(partial) ' : ''}(${p.factsCount ?? 0}f / ${p.decisionsCount ?? 0}d)`;
    case 'memory_delta_absorb':
      return `${String(p.outcome ?? '?')}${p.childJobId ? ` ← ${p.childJobId}` : ''}`;
    case 'memory_snapshot_written': {
      const parts: string[] = [];
      if (typeof p.facts === 'number') parts.push(`${p.facts}f`);
      if (typeof p.decisions === 'number') parts.push(`${p.decisions}d`);
      if (typeof p.openQuestions === 'number') parts.push(`${p.openQuestions}q`);
      const counts = parts.length ? ` (${parts.join('/')})` : '';
      const sizeKb = typeof p.bytes === 'number' ? ` ${(p.bytes / 1024).toFixed(1)}KB` : '';
      return `${String(p.status ?? '?')} → ${String(p.path ?? '?')}${counts}${sizeKb}`;
    }
    case 'memory_snapshot_failed':
      return `${String(p.status ?? '?')} write failed: ${String(p.error ?? '?')}`;
    case 'watchdog_fire':
      return `${String(p.kind2 ?? '')} at iter=${p.iteration ?? '?'}`;
    case 'followup_detected':
      return `movement=${String(p.movementName ?? '?')}`;
    case 'context_action':
      return `${String(p.type ?? '?')} ratio=${typeof p.ratio === 'number' ? (p.ratio * 100).toFixed(0) + '%' : '?'}`;
    case 'transition':
      return `→ ${String(p.nextStep ?? '?')}`;
    case 'complete':
      return String(p.status ?? '?');
    case 'movement_start':
      return `visit ${p.visitCount ?? '?'}/${p.maxVisits ?? '?'}`;
    case 'movement_complete':
      return `→ ${String(p.next ?? p.movement ?? event.movement ?? '?')}`;
    case 'run_start':
      return `piece=${String(p.pieceName ?? '?')}`;
    case 'run_complete': {
      const cancel = p.cancel as { phase?: string; movement?: string } | undefined;
      const cancelInfo = cancel?.phase ? ` cancel:${cancel.phase}@${cancel.movement ?? '?'}` : '';
      const snapshot = p.memorySnapshotPath
        ? ` snapshot:${String(p.memorySnapshotPath).replace(/^logs\//, '')}`
        : '';
      return `${String(p.status ?? '?')}${p.abortReason ? ` (${p.abortReason})` : ''}${cancelInfo}${snapshot}`;
    }
    default:
      return '';
  }
}
