import { describe, expect, it, vi } from 'vitest';
import {
  aggregatePcmPeaks,
  describeWaveform,
  renderWaveformToCanvas,
} from '../../src/waveform';

describe('waveform canvas rendering', () => {
  it('renders bounded columns and supplies a text alternative', () => {
    const peaks = aggregatePcmPeaks(
      {
        container: 'raw',
        data: new Int16Array([-32_768, 0, 16_384, 32_767]),
        encoding: 's16le',
        sampleRate: 2,
      },
      { bucketCount: 4 }
    );
    const { canvas, context, attributes } = createCanvas(200, 80);

    const rendered = renderWaveformToCanvas(canvas, peaks, {
      devicePixelRatio: 2,
      startBucket: 1,
      endBucket: 4,
      selection: { startFraction: 0.25, endFraction: 0.75 },
    });

    expect(canvas.width).toBe(400);
    expect(canvas.height).toBe(160);
    expect(rendered.startBucket).toBe(1);
    expect(rendered.endBucket).toBe(4);
    expect(rendered.accessibleLabel).toContain('Audio waveform');
    expect(attributes.get('role')).toBe('img');
    expect(attributes.get('aria-label')).toBe(rendered.accessibleLabel);
    expect(context.moveTo).toHaveBeenCalled();
    expect(context.lineTo).toHaveBeenCalled();
    expect(context.fillRect).toHaveBeenCalledWith(50, 0, 100, 80);
  });

  it('describes empty waveforms and respects an explicit accessible label', () => {
    const empty = aggregatePcmPeaks(
      {
        container: 'raw',
        data: new Uint8Array(),
        encoding: 's16le',
        sampleRate: 8_000,
      },
      { bucketCount: 1 }
    );
    const { canvas, attributes } = createCanvas(100, 40);
    const result = renderWaveformToCanvas(canvas, empty, {
      accessibleLabel: 'No audible content',
    });

    expect(describeWaveform(empty)).toBe('Empty audio waveform.');
    expect(result.accessibleLabel).toBe('No audible content');
    expect(attributes.get('aria-label')).toBe('No audible content');
  });

  it('rejects unsafe dimensions, ranges, and selections', () => {
    const peaks = aggregatePcmPeaks(
      {
        container: 'raw',
        data: new Int16Array([1, 2]),
        encoding: 's16le',
        sampleRate: 8_000,
      },
      { bucketCount: 2 }
    );
    const { canvas } = createCanvas(100, 40);

    expect(() =>
      renderWaveformToCanvas(canvas, peaks, { width: 100_000 })
    ).toThrow(/safe limits/u);
    expect(() =>
      renderWaveformToCanvas(canvas, peaks, {
        startBucket: 2,
        endBucket: 1,
      })
    ).toThrow(/bucket range/u);
    expect(() =>
      renderWaveformToCanvas(canvas, peaks, {
        selection: { startFraction: -1, endFraction: 0.5 },
      })
    ).toThrow(/selection/u);
  });
});

function createCanvas(width: number, height: number) {
  const attributes = new Map<string, string>();
  const context = {
    setTransform: vi.fn(),
    clearRect: vi.fn(),
    fillRect: vi.fn(),
    beginPath: vi.fn(),
    moveTo: vi.fn(),
    lineTo: vi.fn(),
    stroke: vi.fn(),
    fillStyle: '',
    strokeStyle: '',
    lineWidth: 1,
  };
  const canvas = {
    width,
    height,
    clientWidth: width,
    clientHeight: height,
    getContext: vi.fn(() => context),
    setAttribute: vi.fn((name: string, value: string) => {
      attributes.set(name, value);
    }),
  } as unknown as HTMLCanvasElement;
  return { canvas, context, attributes };
}
