import { NextApiRequest, NextApiResponse } from "next";
import { Controller, Handler, Method } from "./types";

export class Router {
  private _middleWare: Handler<any>[] = [];

  constructor(public controller: Controller = {}) {}

  handle(controller?: Controller) {
    this.controller = { ...this.controller, ...controller };

    return (req: NextApiRequest, res: NextApiResponse) => {
      return new Promise(() => {
        const method: Method = req.method as Method;
        const handlers = this.controller[method] || this.methodNotAllowed;

        Array.isArray(handlers)
          ? this.execRoute(req, res, ...handlers)
          : this.execRoute(req, res, handlers);
      });
    };
  }

  use(...handlers: Handler<any>[]) {
    this._middleWare.push(...handlers);
  }

  private async execRoute(
    req: NextApiRequest,
    res: NextApiResponse,
    ...handlers: Handler<any>[]
  ) {
    if (handlers.length === 0) return this.methodNotAllowed(req, res);

    await this.execMiddleware(req, res);

    for await (const handler of handlers) {
      await new Promise((resolve) => handler(req, res, resolve));
    }
  }

  private async execMiddleware(req: NextApiRequest, res: NextApiResponse) {
    for await (const handler of this._middleWare) {
      await new Promise((resolve) => handler(req, res, resolve));
    }
  }

  private methodNotAllowed(req: NextApiRequest, res: NextApiResponse<string>) {
    res.status(405).end(`Cannot ${req.method} ${req.url}`);
  }
}
