/* eslint-disable no-await-in-loop */
/* eslint-disable func-names */
/* eslint-disable no-param-reassign */
/* eslint-disable arrow-body-style */

interface IRetryMethodProps {
  maxRetry?: number;
  ms?: number;
}

export const RetryMethod = ({ maxRetry = 5, ms = 0 }: IRetryMethodProps = {}) => {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;

    const newMethod = async function (...args: []): Promise<any> {
      let result;

      let innerMaxRetry = maxRetry;
      let goToNexTry = false;

      do {
        try {
          // @ts-ignore
          result = await originalMethod.apply(this, args);
          goToNexTry = false;
        } catch (error) {
          innerMaxRetry -= 1;
          goToNexTry = innerMaxRetry > 0;

          if (!goToNexTry) {
            throw error;
          } else {
            await new Promise((resolve) => setTimeout(resolve, ms));
          }
        }
      } while (goToNexTry);

      return result;
    };

    return {
      ...descriptor,
      value: newMethod,
    };
  };
};
