import { Assert, IsArray, IsNumber } from "@paulpopat/safe-type";
import { Build, Promised } from "./utils";

declare global {
  interface Generator<T = unknown, TReturn = any, TNext = unknown> {
    /**
     * Computes the average of a sequence of number values.
     * @returns The average of the sequence of values.
     */
    average(this: Generator<number, TReturn, TNext>): number;
    /**
     * Computes the average of a sequence of number values that are obtained by invoking a transform function on each element of the input sequence.
     * @returns The average of the sequence of values.
     */
    average(selector: (item: T) => number): number;
  }

  interface AsyncGenerator<T = unknown, TReturn = any, TNext = unknown> {
    average(this: AsyncGenerator<number, TReturn, TNext>): number;
    average(selector: (item: T) => Promised<number>): Promise<number>;
  }
}

function GetAverage(items: number[]) {
  Assert(IsArray(IsNumber), items);
  return items.reduce((c, n) => c + n / items.length, 0);
}

Build(
  "average",
  function (selector) {
    let sel = selector ?? ((i) => i);
    const data = [];
    for (const item of this) {
      data.push(sel(item));
    }

    return GetAverage(data);
  },
  async function (selector) {
    let sel = selector ?? ((i) => i);
    const data = [];
    for await (const item of this) {
      data.push(await Promise.resolve(sel(item)));
    }

    return GetAverage(data);
  }
);
