import { Build } from "./utils";

declare global {
  interface Generator<T = unknown, TReturn = any, TNext = unknown> {
    /**
     * Produces the set intersection of two sequences by using
     * the default equality comparer to compare values.
     * @param that A sequence whose distinct elements that also appear in the first sequence will be returned.
     * @returns A sequence that contains the elements that form the set intersection of two sequences.
     */
    intersect(
      this: Generator<number, TReturn, TNext>,
      that: Generator<number, TReturn, TNext> | number[]
    ): Generator<number, TReturn, TNext>;
    /**
     * Produces the set intersection of two sequences by using
     * the default equality comparer to compare values.
     * @param that A sequence whose distinct elements that also appear in the first sequence will be returned.
     * @returns A sequence that contains the elements that form the set intersection of two sequences.
     */
    intersect(
      this: Generator<string, TReturn, TNext>,
      that: Generator<string, TReturn, TNext> | string[]
    ): Generator<string, TReturn, TNext>;
    /**
     * Produces the set intersection of two sequences by using
     * the default equality comparer to compare values.
     * @param that A sequence whose distinct elements that also appear in the first sequence will be returned.
     * @returns A sequence that contains the elements that form the set intersection of two sequences.
     */
    intersect(
      this: Generator<boolean, TReturn, TNext>,
      that: Generator<boolean, TReturn, TNext> | boolean[]
    ): Generator<boolean, TReturn, TNext>;
    /**
     * Produces the set intersection of two sequences by using
     * the specified comparitor to compare values.
     * @param that A sequence whose distinct elements that also appear in the first sequence will be returned.
     * @param comparitor A comparitor to compare values.
     * @returns A sequence that contains the elements that form the set intersection of two sequences.
     */
    intersect(
      that: Generator<T, TReturn, TNext> | T[],
      comparitor: (a: T, b: T) => boolean
    ): Generator<T, TReturn, TNext>;
  }

  interface AsyncGenerator<T = unknown, TReturn = any, TNext = unknown> {
    /**
     * Produces the set intersection of two sequences by using
     * the default equality comparer to compare values.
     * @param that A sequence whose distinct elements that also appear in the first sequence will be returned.
     * @returns A sequence that contains the elements that form the set intersection of two sequences.
     */
    intersect(
      this: AsyncGenerator<number, TReturn, TNext>,
      that:
        | AsyncGenerator<number, TReturn, TNext>
        | Generator<number, TReturn, TNext>
        | number[]
    ): AsyncGenerator<number, TReturn, TNext>;
    /**
     * Produces the set intersection of two sequences by using
     * the default equality comparer to compare values.
     * @param that A sequence whose distinct elements that also appear in the first sequence will be returned.
     * @returns A sequence that contains the elements that form the set intersection of two sequences.
     */
    intersect(
      this: AsyncGenerator<string, TReturn, TNext>,
      that:
        | AsyncGenerator<string, TReturn, TNext>
        | Generator<string, TReturn, TNext>
        | string[]
    ): AsyncGenerator<string, TReturn, TNext>;
    /**
     * Produces the set intersection of two sequences by using
     * the default equality comparer to compare values.
     * @param that A sequence whose distinct elements that also appear in the first sequence will be returned.
     * @returns A sequence that contains the elements that form the set intersection of two sequences.
     */
    intersect(
      this: AsyncGenerator<boolean, TReturn, TNext>,
      that:
        | AsyncGenerator<boolean, TReturn, TNext>
        | Generator<boolean, TReturn, TNext>
        | boolean[]
    ): AsyncGenerator<boolean, TReturn, TNext>;
    /**
     * Produces the set intersection of two sequences by using
     * the specified comparitor to compare values.
     * @param that A sequence whose distinct elements that also appear in the first sequence will be returned.
     * @param comparitor A comparitor to compare values.
     * @returns A sequence that contains the elements that form the set intersection of two sequences.
     */
    intersect(
      that:
        | AsyncGenerator<T, TReturn, TNext>
        | Generator<T, TReturn, TNext>
        | T[],
      comparitor: (a: T, b: T) => boolean
    ): AsyncGenerator<T, TReturn, TNext>;
  }
}

Build(
  "intersect",
  function* (that, comparitor = (a: unknown, b: unknown) => a === b) {
    const data = Array.isArray(that) ? that : that.array();
    for (const item of this) {
      let valid = false;
      for (const part of data) {
        if (comparitor(item, part)) {
          valid = true;
          break;
        }
      }

      if (valid) {
        yield item;
      }
    }
  },
  async function* (that, comparitor = (a: unknown, b: unknown) => a === b) {
    const data = Array.isArray(that) ? that : await that.array();
    const existing: unknown[] = [];
    for await (const item of this) {
      let valid = false;
      for (const part of data) {
        if (await Promise.resolve(comparitor(item, part))) {
          valid = true;
          break;
        }
      }

      if (valid) {
        existing.push(item);
        yield item;
      }
    }
  }
);
