function isPlainObject(value) {
  return (
    value !== null &&
    typeof value === "object" &&
    !Array.isArray(value) &&
    Object.prototype.toString.call(value) === "[object Object]"
  );
}

function errorCodeFromStatus(statusCode) {
  switch (statusCode) {
    case 400:
      return "bad_request";
    case 401:
      return "unauthorized";
    case 403:
      return "forbidden";
    case 404:
      return "not_found";
    case 409:
      return "conflict";
    case 413:
      return "payload_too_large";
    case 415:
      return "unsupported_media_type";
    case 422:
      return "unprocessable_entity";
    case 429:
      return "rate_limited";
    case 500:
      return "internal_error";
    default:
      return statusCode >= 500 ? "internal_error" : "request_error";
  }
}

function normalizeErrorPayload(payload, statusCode) {
  if (statusCode < 400) return payload;

  if (typeof payload === "string") {
    return {
      error: {
        code: errorCodeFromStatus(statusCode),
        message: payload,
      },
    };
  }

  if (!isPlainObject(payload)) {
    return {
      error: {
        code: errorCodeFromStatus(statusCode),
        message: "Request failed",
      },
    };
  }

  if (isPlainObject(payload.error)) {
    const code = payload.error.code || errorCodeFromStatus(statusCode);
    const message = payload.error.message || "Request failed";
    return {
      ...payload,
      error: {
        ...payload.error,
        code,
        message,
      },
    };
  }

  if (typeof payload.error === "string") {
    const { error, ...rest } = payload;
    return {
      ...rest,
      error: {
        code: errorCodeFromStatus(statusCode),
        message: error,
      },
    };
  }

  if (typeof payload.message === "string") {
    const { message, ...rest } = payload;
    return {
      ...rest,
      error: {
        code: errorCodeFromStatus(statusCode),
        message,
      },
    };
  }

  return {
    ...payload,
    error: {
      code: errorCodeFromStatus(statusCode),
      message: "Request failed",
    },
  };
}

function sendError(res, statusCode, message, code, extra = {}) {
  return res.status(statusCode).json({
    ...extra,
    error: {
      code: code || errorCodeFromStatus(statusCode),
      message,
    },
  });
}

module.exports = {
  errorCodeFromStatus,
  normalizeErrorPayload,
  sendError,
};
