import type { NextApiRequest, NextApiResponse } from 'next';

import fetchFactory from 'lib/api/fetch';
import getUrlWithNetwork from 'lib/api/getUrlWithNetwork';
import { httpLogger } from 'lib/api/logger';

type Methods = 'GET' | 'POST' | 'PUT' | 'DELETE';

export default function createHandler(getUrl: (_req: NextApiRequest) => string, allowedMethods: Array<Methods>, apiEndpoint?: string) {
  const handler = async(_req: NextApiRequest, res: NextApiResponse) => {
    httpLogger(_req, res);

    if (!_req.method || !allowedMethods.includes(_req.method as Methods)) {
      res.setHeader('Allow', allowedMethods);
      res.status(405).end(`Method ${ _req.method } Not Allowed`);
      return;
    }

    const isBodyDisallowed = _req.method === 'GET' || _req.method === 'HEAD';

    const url = apiEndpoint ? `/api${ getUrl(_req) }` : getUrlWithNetwork(_req, `/api${ getUrl(_req) }`);
    const fetch = fetchFactory(_req, apiEndpoint);
    const response = await fetch(url, {
      method: _req.method,
      body: isBodyDisallowed ? undefined : _req.body,
    });

    if (response.status === 200) {
      const data = await response.json();
      res.status(200).json(data);
      return;
    }

    let responseError;
    const defaultError = { statusText: response.statusText, status: response.status };

    try {
      const error = await response.json() as { errors: unknown };
      responseError = error?.errors || defaultError;
    } catch (error) {
      responseError = defaultError;
    }

    httpLogger.logger.error({ err: responseError, url: _req.url });

    res.status(500).json(responseError);
  };

  return handler;
}
