import type {Arg0, Ret, AsFlatList, Fn} from './defs.js';

/**
 * Returns a wrapped identity function — `gen()` with no arguments produces a
 * function that yields its input as a single value. Rarely used directly.
 * @returns an async generator function whose result is an `AsyncGenerator` yielding the input.
 */
declare function gen(): (arg: unknown) => AsyncGenerator<unknown, void, unknown>;
/**
 * Returns an async generator function that applies the given functions in sequence,
 * handling all special return values (`none`, `stop`, `many`, `finalValue`, `flushable`).
 * Nested function lists are inlined. Falsy items are filtered out.
 * @param fns functions to be composed: regular, async, generator, async generator, or
 *   nested arrays/function-lists. Falsy items are ignored.
 * @returns an async generator function whose call yields each output value of the composed pipeline.
 */
declare function gen<L extends readonly unknown[]>(
  ...fns: gen.FnList<Arg0<L>, L>
): AsFlatList<L> extends readonly [Fn, ...Fn[]]
  ? (arg: Arg0<L>) => AsyncGenerator<Ret<L>, void, unknown>
  : (arg: unknown) => AsyncGenerator<unknown, void, unknown>;

declare namespace gen {
  /**
   * Returns a type, which was expected from a list item.
   * It is used to highlight mismatches between argument types and return types in a list.
   */
  type FnItem<I, F> = F extends readonly [infer F1, ...infer R]
    ? F1 extends null | undefined
      ? readonly [F1, ...FnList<I, R>]
      : readonly [FnItem<I, F1>, ...FnList<Ret<F1, I>, R>]
    : F extends readonly unknown[]
      ? readonly [FnItem<I, any>]
      : F extends Fn
        ? I extends Arg0<F>
          ? F
          : (arg: I, ...rest: readonly unknown[]) => ReturnType<F>
        : F extends null | undefined
          ? F
          : never;

  /**
   * Replicates a tuple verifying the types of the list items so arguments match returns.
   * The replicated tuple is used to highlight mismatches between list items.
   */
  type FnList<I, L> = L extends readonly [infer F1, ...infer R]
    ? F1 extends null | undefined
      ? readonly [F1, ...FnList<I, R>]
      : readonly [FnItem<I, F1>, ...FnList<Ret<F1, I>, R>]
    : L;
}

export default gen;
export {gen};
