// Import from NodeJS
import * as timers from 'timers/promises';

export class PollingError extends Error {
  public constructor(message?: string) {
    super(message);

    this.name = 'PollingError';
    // See SO post: https://stackoverflow.com/a/48342359
    // restore prototype chain
    const actualProto = new.target.prototype;
    Object.setPrototypeOf(this, actualProto);
  }
}

/**
 * Function for polling a given resource.
 * @author Benedikt Arnarsson
 * @param opts.callResource the function for calling the external resource
 * @param opts.validateResult validate that we got the result we want from the resource
 * @param opts.waitForMS time between calls to the resource (in milliseconds)
 * @param opts.maxAttempts the maximum number of attempts (defaults to 'infinity')
 */
const pollResource = async <T>(
  opts: {
    callResource: () => Promise<T>,
    validateResult: (res: T) => boolean,
    waitForMS: number,
    timeOut?: number,
  },
) => {
  const {
    callResource,
    validateResult,
    waitForMS,
    timeOut,
  } = opts;

  for (let t = 0; t <= timeOut; t += waitForMS) {
    const result = await callResource();
    if (validateResult(result)) {
      return result;
    }

    await timers.setTimeout(waitForMS);
  }
  // FIXME: better error message?
  throw new PollingError(`Exceeded timeout of ${timeOut}.
With wait interval of ${waitForMS}. 
With polling function:
${callResource.toString()}`);
};

export default pollResource;
