// Copyright 2024 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only

import { assert } from 'chai';
import { Readable } from 'node:stream';
import { once } from 'node:events';
import { toWebStream } from '../../util/toWebStream';

describe('toWebStream', () => {
  it('only reads what it needs', async () => {
    const CHUNK_SIZE = 16 * 1024;
    let pushed = 0;
    const readable = new Readable({
      highWaterMark: CHUNK_SIZE,
      read() {
        pushed += 1;
        this.push(Buffer.alloc(CHUNK_SIZE));
      },
    });

    const reader = toWebStream(readable).getReader();
    const { value, done } = await reader.read();

    // One to be read, one buffered
    assert.strictEqual(pushed, 2);
    assert.isFalse(done);
    assert.strictEqual(value?.byteLength, 2 * CHUNK_SIZE);
  });

  it('closes controller on end', async () => {
    const readable = Readable.from([
      Buffer.from('hello '),
      Buffer.from('world'),
    ]);

    const reader = toWebStream(readable).getReader();
    {
      const { value, done } = await reader.read();
      assert.strictEqual(value?.toString(), 'hello ');
      assert.isFalse(done);
    }
    {
      const { value, done } = await reader.read();
      assert.strictEqual(value?.toString(), 'world');
      assert.isFalse(done);
    }
    {
      const { value, done } = await reader.read();
      assert.isUndefined(value);
      assert.isTrue(done);
    }
  });

  it('handles premature close', async () => {
    const readable = new Readable({
      read() {
        // no-op
      },
    });

    const reader = toWebStream(readable).getReader();
    readable.destroy();
    await assert.isRejected(reader.read(), 'Premature close');
  });

  it('handles error close', async () => {
    const readable = new Readable({
      read() {
        // no-op
      },
    });

    const reader = toWebStream(readable).getReader();
    readable.destroy(new Error('error msg'));
    await assert.isRejected(reader.read(), 'error msg');
  });

  it('can be wrapped and destroyed during data read', async () => {
    const readable = new Readable({
      read() {
        this.push(Buffer.from('hello'));
      },
    });

    const web = toWebStream(readable);

    // Some sort of mismatch between Node's expectation for ReadStream and
    // what TS says ReadStream is in WebAPIs.
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const node = Readable.fromWeb(web as any);
    node.on('data', () => {
      node.destroy();
    });
    await once(node, 'close');
  });
});
