import { Build, Promised } from "./utils";

declare global {
  interface Generator<T = unknown, TReturn = any, TNext = unknown> {
    /**
     * Creates a dictionary from a generator according to a
     * specified key selector function.
     * @param key_selector A function to extract a key from each element.
     * @returns A dictionary that contains values of type selected from the input sequence.
     */
    dictionary<TKey extends number | string>(
      key_selector: (item: T) => TKey
    ): Record<TKey, T>;
    /**
     * Creates a dictionary from a generator according to
     * specified key selector and element selector functions.
     * @param key_selector A function to extract a key from each element.
     * @param value_selector A transform function to produce a result element value from each element.
     * @returns A dictionary that contains values of type selected from the input sequence.
     */
    dictionary<TKey extends number | string, TResult>(
      key_selector: (item: T) => TKey,
      value_selector: (item: T) => TResult
    ): Record<TKey, TResult>;
  }

  interface AsyncGenerator<T = unknown, TReturn = any, TNext = unknown> {
    /**
     * Creates a dictionary from a generator according to a
     * specified key selector function.
     * @param key_selector A function to extract a key from each element.
     * @returns A dictionary that contains values of type selected from the input sequence.
     */
    dictionary<TKey extends number | string>(
      key_selector: (item: T) => Promised<TKey>
    ): Promise<Record<TKey, T>>;
    /**
     * Creates a dictionary from a generator according to
     * specified key selector and element selector functions.
     * @param key_selector A function to extract a key from each element.
     * @param value_selector A transform function to produce a result element value from each element.
     * @returns A dictionary that contains values of type selected from the input sequence.
     */
    dictionary<TKey extends number | string, TResult>(
      key_selector: (item: T) => Promised<TKey>,
      value_selector: (item: T) => Promised<TResult>
    ): Promise<Record<TKey, TResult>>;
  }
}

Build(
  "dictionary",
  function (key_selector, value_selector = (i) => i) {
    const result: Record<string | number, unknown> = {};
    for (const item of this) {
      result[key_selector(item)] = value_selector(item);
    }

    return result;
  },
  async function (key_selector, value_selector = (i) => i) {
    const result: Record<string | number, unknown> = {};
    for await (const item of this) {
      result[await Promise.resolve(key_selector(item))] = await Promise.resolve(
        value_selector(item)
      );
    }

    return result;
  }
);
