
// https://github.com/koajs/compose/blob/master/index.js
/**
 * @typeParam T - Type of context passed through the middleware
 * @param middleware middleware stack
 * @return {Callback}
 */
export function compose<T>(...middleware: (((ctx: T, next: () => Promise<void>) => Promise<void>) | ((ctx: T) => Promise<void>))[]): (ctx: T) => Promise<void> {
    if (!Array.isArray(middleware)) {
        throw new Error('middleware must be array!');
    }
    for (const fn of middleware) {
        if (typeof fn !== 'function') {
            throw new Error('middleware must be compose of functions!');
        }
    }
    return async function (ctx: T, next?: () => Promise<void>) {
        let index = -1;
        return await dispatch(0);

        async function dispatch(i: number): Promise<void> {
            if (i < index) {
                throw new Error('next() called multiple times');
            }
            index = i;
            let fn: ((() => Promise<void>) | undefined) | ((c: T, n: () => Promise<void>) => Promise<void>);
            if (i === middleware.length) {
                fn = next;
            }
            else {
                fn = middleware[i];
            }
            if (!fn) {
                return;
            }

            await fn(ctx, dispatch.bind(null, i + 1));
        }
    }
}
