/**
 * Pure data-shaping helpers for the `@bytelyst/charts` / `@bytelyst/data-viz`
 * migration (UX-2). Kept framework-free so they are unit-testable in the node
 * vitest env and guarantee finite, NaN-free output before it ever reaches an
 * SVG chart (the chart primitives also filter non-finite values defensively).
 */
import type { BarDatum } from '@bytelyst/charts';
import type { DonutSlice } from '@bytelyst/charts';

/** Coerce to a finite number, falling back to 0 for null/NaN/Infinity. */
export function finite(n: unknown): number {
  const v = typeof n === 'number' ? n : Number(n);
  return Number.isFinite(v) ? v : 0;
}

/** Map a list of rows to a finite numeric series (X = array index). */
export function seriesValues<T>(rows: readonly T[], key: keyof T): number[] {
  return rows.map(r => finite(r[key]));
}

/**
 * Map dated rows to `BarDatum[]`, showing an X label only every `labelEvery`
 * bars (date `MM-DD`) so dense 30/90-day series don't overlap. Empty string
 * suppresses a label without dropping the bar.
 */
export function dateBars<T extends { date: string }>(
  rows: readonly T[],
  valueKey: keyof T,
  labelEvery = 5
): BarDatum[] {
  return rows.map((r, i) => ({
    id: r.date,
    value: finite(r[valueKey]),
    label: i % labelEvery === 0 ? String(r.date).slice(5) : '',
  }));
}

/**
 * Map breakdown rows to `DonutSlice[]`, dropping non-positive slices (a Donut
 * of all-zero data renders an empty muted ring rather than NaN arcs).
 */
export function donutSlices<T>(
  rows: readonly T[],
  idKey: keyof T,
  valueKey: keyof T
): DonutSlice[] {
  return rows
    .map(r => ({ id: String(r[idKey]), label: String(r[idKey]), value: finite(r[valueKey]) }))
    .filter(s => s.value > 0);
}
