import { describe, expect, it } from 'vitest';
import { renderToStaticMarkup } from 'react-dom/server';
import { createElement } from 'react';
import { AreaChart, BarChart, Donut } from '@bytelyst/charts';
import { finite, seriesValues, dateBars, donutSlices } from '@/lib/chart-data';

/**
 * UX-2 — charts migration guard.
 *
 * Two layers:
 *  1. the pure data-shaping helpers always produce finite output, and
 *  2. the migrated `@bytelyst/charts` primitives render valid SVG with no
 *     literal `NaN` in any coordinate — including the degenerate inputs that
 *     used to silently break recharts (empty series, single point, all-zero,
 *     and non-finite values).
 */

describe('chart-data helpers', () => {
  it('coerces non-finite values to 0', () => {
    expect(finite(5)).toBe(5);
    expect(finite('7')).toBe(7);
    expect(finite(NaN)).toBe(0);
    expect(finite(Infinity)).toBe(0);
    expect(finite(null)).toBe(0);
    expect(finite(undefined)).toBe(0);
  });

  it('maps rows to a finite numeric series', () => {
    const rows = [{ v: 1 }, { v: 2 }, { v: Number.NaN }];
    expect(seriesValues(rows, 'v')).toEqual([1, 2, 0]);
  });

  it('labels only every Nth dated bar but keeps every bar', () => {
    const rows = Array.from({ length: 7 }, (_, i) => ({ date: `2026-05-0${i + 1}`, n: i }));
    const bars = dateBars(rows, 'n', 5);
    expect(bars).toHaveLength(7);
    expect(bars[0].label).toBe('05-01');
    expect(bars[1].label).toBe('');
    expect(bars[5].label).toBe('05-06');
    expect(bars.every(b => Number.isFinite(b.value))).toBe(true);
  });

  it('drops non-positive donut slices', () => {
    const rows = [
      { k: 'a', v: 3 },
      { k: 'b', v: 0 },
      { k: 'c', v: -1 },
    ];
    const slices = donutSlices(rows, 'k', 'v');
    expect(slices.map(s => s.id)).toEqual(['a']);
  });
});

describe('migrated charts render finite SVG', () => {
  const series: number[][] = [
    [10, 20, 15, 30, 25],
    [], // empty
    [42], // single point
    [0, 0, 0], // all-zero
    [Number.NaN, 5, Number.POSITIVE_INFINITY, 8], // non-finite mixed
  ];

  it.each(series.map((s, i) => [i, s] as const))('AreaChart case %i renders no NaN', (_i, s) => {
    const html = renderToStaticMarkup(createElement(AreaChart, { values: s, ariaLabel: 'a' }));
    expect(html).toContain('<svg');
    expect(html).not.toContain('NaN');
  });

  it.each(series.map((s, i) => [i, s] as const))('BarChart case %i renders no NaN', (_i, s) => {
    // Mirror the admin data path: bar values are always sanitized via `finite`.
    const data = s.map((v, idx) => ({ id: `d${idx}`, value: finite(v), label: `d${idx}` }));
    const html = renderToStaticMarkup(createElement(BarChart, { data, ariaLabel: 'b' }));
    expect(html).toContain('<svg');
    expect(html).not.toContain('NaN');
  });

  it.each(series.map((s, i) => [i, s] as const))('Donut case %i renders no NaN', (_i, s) => {
    // Mirror the admin data path: slices always come from `donutSlices`, which
    // drops non-positive/non-finite values.
    const slices = donutSlices(
      s.map((v, idx) => ({ k: `s${idx}`, v })),
      'k',
      'v'
    );
    const html = renderToStaticMarkup(createElement(Donut, { slices, ariaLabel: 'd' }));
    expect(html).toContain('<svg');
    expect(html).not.toContain('NaN');
  });
});
