import type { FlowSource } from "./FlowSource";
import { toStream } from "./froms";
import type { SourcesType } from "./SourcesType";
import { streamAsyncIterator } from "./streamAsyncIterator";

/**
 * return a readable stream that merges streams from sources
 * don't get confused with merges
 * mergeStream: returns a ReadableStream, which doesnt have upstream
 * merges     : returns a TransformStream, which also merges upstream
 */

export const mergeStream: {
  // <T>(...streams: FlowSource<T>[]): ReadableStream<T>;
  <T, SRCS extends FlowSource<T>[]>(
    ...streams: SRCS
  ): ReadableStream<SourcesType<SRCS>>;
} = (...srcs: FlowSource<any>[]): ReadableStream<any> => {
  if (!srcs.length) return new ReadableStream({ start: (c) => c.close() });
  // no nesscerry to merge
  if (srcs.length === 1) return toStream(srcs[0]!);

  const t = new TransformStream();
  const w = t.writable.getWriter();
  const streams = srcs.map(toStream);
  Promise.all(
    streams.map(async (s) => {
      for await (const chunk of Object.assign(s, {
        [Symbol.asyncIterator]: streamAsyncIterator,
      }))
        await w.write(chunk);
    }),
  )
    .then(async () => w.close())
    .catch((error) => {
      streams.forEach((e) => void e.cancel(error).catch(() => {}));
      return w.abort(error).catch(() => {});
    });

  return t.readable;
};
