import { describe, it, expect } from 'vitest';
import { parseNdjsonStream } from './ndjson.js';

function createReadableStream(chunks: string[]): ReadableStream<Uint8Array> {
  const encoder = new TextEncoder();
  let index = 0;
  return new ReadableStream({
    pull(controller) {
      if (index < chunks.length) {
        controller.enqueue(encoder.encode(chunks[index]));
        index++;
      } else {
        controller.close();
      }
    },
  });
}

describe('parseNdjsonStream', () => {
  it('parses complete NDJSON lines', async () => {
    const stream = createReadableStream(['{"a":1}\n{"a":2}\n{"a":3}\n']);
    const results: unknown[] = [];
    for await (const chunk of parseNdjsonStream(stream)) {
      results.push(chunk);
    }
    expect(results).toEqual([{ a: 1 }, { a: 2 }, { a: 3 }]);
  });

  it('handles partial lines split across chunks', async () => {
    const stream = createReadableStream(['{"a":', '1}\n{"a":2}\n']);
    const results: unknown[] = [];
    for await (const chunk of parseNdjsonStream(stream)) {
      results.push(chunk);
    }
    expect(results).toEqual([{ a: 1 }, { a: 2 }]);
  });

  it('skips empty lines', async () => {
    const stream = createReadableStream(['{"a":1}\n\n\n{"a":2}\n']);
    const results: unknown[] = [];
    for await (const chunk of parseNdjsonStream(stream)) {
      results.push(chunk);
    }
    expect(results).toEqual([{ a: 1 }, { a: 2 }]);
  });

  it('skips malformed JSON lines', async () => {
    const stream = createReadableStream(['{"a":1}\nnot json\n{"a":2}\n']);
    const results: unknown[] = [];
    for await (const chunk of parseNdjsonStream(stream)) {
      results.push(chunk);
    }
    expect(results).toEqual([{ a: 1 }, { a: 2 }]);
  });

  it('processes remaining buffer after stream ends', async () => {
    const stream = createReadableStream(['{"a":1}\n{"a":2}']);
    const results: unknown[] = [];
    for await (const chunk of parseNdjsonStream(stream)) {
      results.push(chunk);
    }
    expect(results).toEqual([{ a: 1 }, { a: 2 }]);
  });

  it('handles empty stream', async () => {
    const stream = createReadableStream([]);
    const results: unknown[] = [];
    for await (const chunk of parseNdjsonStream(stream)) {
      results.push(chunk);
    }
    expect(results).toEqual([]);
  });
});
