/**
 * 重试器
 *
 * 执行目标函数，如果目标函数抛出异常会自动重试并返回
 *
 * 注：如果重试失败会抛出最后一次异常，需要捕获异常
 * @param count 重试次数
 * @param delay 每次失败后延迟重试的时间(毫秒)
 * @param errorHandler 中途发生异常的异常处理
 * @returns
 */
export function retry(
  fn: () => Promise<any> | any,
  count: number,
  delay?: number,
  errorHandler?: (error: any) => void
): Promise<any> {
  return new Promise(async (resolve, reject) => {
    try {
      const res = await fn();
      resolve(res);
    } catch (error) {
      if (count <= 0) {
        reject(error);
      } else {
        if (typeof errorHandler === "function") errorHandler(error);

        setTimeout(() => {
          resolve(retry(fn, --count, delay, errorHandler));
        }, delay);
      }
    }
  });
}

/**
 * 重试装饰器
 *
 * @param count 重试次数
 * @param delay 每次失败后延迟重试的时间(毫秒)
 * @param errorHandler 异常处理
 * @returns
 */
export function Retryable(
  count = 1,
  delay: number,
  errorHandler?: (error: any) => void
): MethodDecorator {
  return function (
    target: Object,
    propertyKey: string | symbol,
    descriptor: PropertyDescriptor
  ) {
    const method = descriptor.value;

    descriptor.value = async function () {
      try {
        const res = await retry(
          () => {
            return method.apply(this, arguments);
          },
          count,
          delay,
          errorHandler
        );

        return res;
      } catch (error) {
        if (typeof errorHandler === "function") errorHandler(error);
      }
    };
  };
}
