// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.

/** Utility for representing n-tuple. Used in {@linkcode tee}. */
export type Tuple<T, N extends number> = N extends N
  ? number extends N ? T[] : TupleOf<T, N, []>
  : never;

/** Utility for representing n-tuple of. Used in {@linkcode Tuple}. */
export type TupleOf<T, N extends number, R extends unknown[]> =
  R["length"] extends N ? R
    : TupleOf<T, N, [T, ...R]>;

interface QueueNode<T> {
  value: T;
  next: QueueNode<T> | undefined;
}

class Queue<T> {
  #source: AsyncIterator<T>;
  #queue: QueueNode<T>;
  head: QueueNode<T>;

  done: boolean;

  constructor(iterable: AsyncIterable<T>) {
    this.#source = iterable[Symbol.asyncIterator]();
    this.#queue = {
      value: undefined!,
      next: undefined,
    };
    this.head = this.#queue;
    this.done = false;
  }

  async next() {
    const result = await this.#source.next();
    if (!result.done) {
      const nextNode: QueueNode<T> = {
        value: result.value,
        next: undefined,
      };
      this.#queue.next = nextNode;
      this.#queue = nextNode;
    } else {
      this.done = true;
    }
  }
}

/**
 * Branches the given async iterable into the `n` branches.
 *
 * @example Usage
 * ```ts
 * import { tee } from "@std/async/tee";
 * import { assertEquals } from "@std/assert/assert-equals";
 *
 * const gen = async function* gen() {
 *   yield 1;
 *   yield 2;
 *   yield 3;
 * };
 *
 * const [branch1, branch2] = tee(gen());
 *
 * const result1 = await Array.fromAsync(branch1);
 * assertEquals(result1, [1, 2, 3]);
 *
 * const result2 = await Array.fromAsync(branch2);
 * assertEquals(result2, [1, 2, 3]);
 * ```
 *
 * @typeParam T The type of the provided async iterable and the returned async iterables.
 * @typeParam N The amount of branches to tee into.
 * @param iterable The iterable to tee.
 * @param n The amount of branches to tee into.
 * @returns The tuple where each element is an async iterable.
 */
export function tee<T, N extends number = 2>(
  iterable: AsyncIterable<T>,
  n: N = 2 as N,
): Tuple<AsyncIterable<T>, N> {
  const queue = new Queue<T>(iterable);

  async function* generator(): AsyncGenerator<T> {
    let buffer = queue.head;
    while (true) {
      if (buffer.next) {
        buffer = buffer.next;
        yield buffer.value;
      } else if (queue.done) {
        return;
      } else {
        await queue.next();
      }
    }
  }

  return Array.from({ length: n }).map(
    () => generator(),
  ) as Tuple<
    AsyncIterable<T>,
    N
  >;
}

// denoCacheMetadata={"headers":{"access-control-allow-origin":"*","etag":"\"4785edbdb117eccddc2024ff19422f04\"","x-robots-tag":"noindex","cf-ray":"a223e6564ec37db6-LAX","accept-ranges":"bytes","vary":"Origin","content-security-policy":"default-src 'none'; script-src 'none'; style-src 'none'; img-src 'none'; font-src 'none'; connect-src 'none'; frame-src 'none'; object-src 'none'; frame-ancestors 'none'; sandbox; form-action 'none';","x-jsr-backend":"modules","alt-svc":"h3=\":443\"; ma=86400","access-control-expose-headers":"*","nel":"{\"report_to\":\"cf-nel\",\"success_fraction\":0.0,\"max_age\":604800}","date":"Tue, 28 Jul 2026 12:31:14 GMT","content-type":"text/typescript","set-cookie":"__cf_bm=CFMm2qhMq787DY1JO2MeP383lraxrCkQoC4Hg1Y_bDo-1785241874.9431648-1.0.1.1-8CSFEN6LcfFgc7ADQdEIl5sjXTkFPuzJfqRuIID7jzc9SsDQLAaRI63gck8IwECGBbHcz9heLd0wiMJip8ThApHQrTMuP51dpWvWtDnbTp5RGtr9ys2vupjH4CE.VRwJ; HttpOnly; SameSite=None; Secure; Path=/; Domain=jsr.io; Expires=Tue, 28 Jul 2026 13:01:14 GMT","report-to":"{\"group\":\"cf-nel\",\"max_age\":604800,\"endpoints\":[{\"url\":\"https://a.nel.cloudflare.com/report/v4?s=lH7%2FvfqYXsrCuiqkVCMGp3nek8n09jMIpMyrs2fNJjRIIBMTpZmcXK4R6ACABOZuxMIXLSZoUPnpK4KzNiZpALlhYOBdzLbqM7Qj%2Fgp32V0f%2F%2F1CeBplDN8%3D\"}]}","x-content-type-options":"nosniff","cf-cache-status":"HIT","server":"cloudflare","content-length":"2595","cache-control":"public, max-age=31536000, immutable","cross-origin-resource-policy":"cross-origin","age":"4145"},"url":"https://jsr.io/@std/async/0.224.2/tee.ts","time":1785241874}