import { describe, it, expect } from 'vitest';
import { planChunks } from './chunking';

const SR = 16000;

describe('planChunks', () => {
  it('returns [] for empty or invalid lengths', () => {
    expect(planChunks(0)).toEqual([]);
    expect(planChunks(-5)).toEqual([]);
  });

  it('emits a single chunk covering everything when it fits one window', () => {
    const total = 10 * SR;
    const p = planChunks(total, { windowSec: 28, overlapSec: 2, sampleRate: SR });
    expect(p).toHaveLength(1);
    expect(p[0]).toMatchObject({
      index: 0,
      startSample: 0,
      endSample: total,
      isFirst: true,
      isLast: true,
    });
    expect(p[0]!.endSec).toBeCloseTo(10);
  });

  it('windows long audio with the right stride and full coverage', () => {
    const total = 100 * SR;
    const windowSamples = 28 * SR;
    const stride = (28 - 2) * SR;
    const p = planChunks(total, { windowSec: 28, overlapSec: 2, sampleRate: SR });

    expect(p.length).toBeGreaterThan(1);
    p.forEach((c, i) => {
      expect(c.index).toBe(i);
      expect(c.startSample).toBe(i * stride);
      expect(c.endSample).toBe(Math.min(i * stride + windowSamples, total));
    });

    // exactly one first, exactly one last; last reaches the end
    expect(p.filter((c) => c.isFirst)).toHaveLength(1);
    expect(p.filter((c) => c.isLast)).toHaveLength(1);
    expect(p[p.length - 1]!.endSample).toBe(total);

    // adjacent chunks genuinely overlap
    for (let i = 1; i < p.length; i++) {
      expect(p[i]!.startSample).toBeLessThan(p[i - 1]!.endSample);
    }
  });

  it('rejects nonsensical window/overlap', () => {
    expect(() => planChunks(SR, { windowSec: 0 })).toThrow();
    expect(() => planChunks(SR, { windowSec: 5, overlapSec: 5 })).toThrow();
    expect(() => planChunks(SR, { windowSec: 5, overlapSec: -1 })).toThrow();
  });
});
