import { describe, expect, it } from '@jest/globals';

import { planBatches } from '../../utils/batchPlanner';

describe('planBatches', () => {
  it('returns no batches for empty input', () => {
    expect(planBatches([], { maxChars: 10, maxCount: 10 })).toEqual([]);
  });

  it('keeps a string that exceeds maxChars as its own batch', () => {
    const oversized = { id: 'oversized', content: '123456' };

    expect(planBatches([oversized], { maxChars: 5, maxCount: 10 })).toEqual([[oversized]]);
  });

  it('packs mixed sizes greedily in order', () => {
    const strings = [
      { id: 'one', content: '12' },
      { id: 'two', content: '345' },
      { id: 'three', content: '6789' },
    ];

    expect(planBatches(strings, { maxChars: 5, maxCount: 10 })).toEqual([
      [strings[0], strings[1]],
      [strings[2]],
    ]);
  });

  it('allows a string that exactly fills maxChars', () => {
    const exact = { id: 'exact', content: '12345' };
    const next = { id: 'next', content: '6' };

    expect(planBatches([exact, next], { maxChars: 5, maxCount: 10 })).toEqual([
      [exact],
      [next],
    ]);
  });

  it('starts a new batch at the maxCount boundary', () => {
    const strings = [
      { id: 'one', content: 'a' },
      { id: 'two', content: 'b' },
      { id: 'three', content: 'c' },
    ];

    expect(planBatches(strings, { maxChars: 100, maxCount: 2 })).toEqual([
      [strings[0], strings[1]],
      [strings[2]],
    ]);
  });

  it('starts a new batch before the JSON payload exceeds maxPayloadBytes', () => {
    const strings = [
      { id: 'one', content: 'a' },
      { id: 'two', content: 'b' },
      { id: 'three', content: 'c' },
    ];
    const pairPayloadBytes = Buffer.byteLength(JSON.stringify(strings.slice(0, 2)));
    const maxPayloadBytes = pairPayloadBytes - 1;
    const batches = planBatches(strings, { maxChars: 100, maxCount: 10, maxPayloadBytes });

    expect(pairPayloadBytes).toBeGreaterThan(maxPayloadBytes);
    expect(batches).toEqual([
      [strings[0]],
      [strings[1]],
      [strings[2]],
    ]);
    expect(batches.every((batch) => Buffer.byteLength(JSON.stringify(batch)) <= maxPayloadBytes)).toBe(true);
  });

  it('preserves order and retains every input string', () => {
    const strings = [
      { id: 'first', content: '123' },
      { id: 'second', content: '4567' },
      { id: 'third', content: '89' },
      { id: 'fourth', content: '0' },
    ];

    const batches = planBatches(strings, { maxChars: 5, maxCount: 2 });

    expect(batches.flat()).toEqual(strings);
    expect(batches.flat().map((string) => string.id)).toEqual(
      strings.map((string) => string.id)
    );
  });
});
