import { PatternMatch } from "@paulpopat/safe-type";

export type Promised<T> = Promise<T> | T;

type SGen = Generator<unknown, unknown, unknown>;
type AGen = AsyncGenerator<unknown, unknown, unknown>;

type Gen = SGen | AGen;

const Generator: Gen = Object.getPrototypeOf(async function* () {}).__proto__;

function IsAsync(generator: any): generator is AGen {
  return Symbol.asyncIterator in generator;
}

function IsSync(generator: any): generator is SGen {
  return Symbol.iterator in generator;
}

export function Build<TKey extends keyof Gen>(
  key: TKey,
  sync_handler: (
    this: SGen,
    ...args: Parameters<SGen[TKey]>
  ) => ReturnType<SGen[TKey]>,
  async_handler: (
    this: AGen,
    ...args: Parameters<AGen[TKey]>
  ) => ReturnType<AGen[TKey]>
) {
  const gen: any = Generator;
  if (gen[key]) {
    return;
  }

  gen[key] = function (...args: Parameters<Gen[TKey]>) {
    if (!IsSync(this) && !IsAsync(this)) return;

    return PatternMatch(IsSync, IsAsync)(
      (s) => (sync_handler as any).bind(s, ...args)(),
      (a) => (async_handler as any).bind(a, ...args)()
    )(this);
  };
}
