"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TimeoutError = void 0;
exports.promiseWithTimeout = promiseWithTimeout;
exports.filterAsync = filterAsync;
exports.awaitResult = awaitResult;
const async_retry_1 = __importDefault(require("async-retry"));
const loggingProvider_1 = require("../providers/logging/loggingProvider");
const pureUtil_1 = require("./pureUtil");
const logUtils_1 = require("./logUtils");
async function promiseWithTimeout(promise, timeoutMillis, description, timeoutType = 'mabl timeout', printToConsole = true) {
    const startTimeMillis = new Date().getTime();
    const { timer, cancelTimer, errorMessage } = startTimer(timeoutMillis, description, timeoutType);
    try {
        const result = await Promise.race([timer, promise]);
        if (loggingProvider_1.logger.isDebugEnabled() && printToConsole) {
            loggingProvider_1.logger.debug(`"${description}" completed in ${new Date().getTime() - startTimeMillis}ms`);
        }
        return result;
    }
    catch (error) {
        if (error instanceof TimeoutError &&
            printToConsole &&
            error.message === errorMessage) {
            loggingProvider_1.logger.warn(error.message);
        }
        throw error;
    }
    finally {
        cancelTimer();
    }
}
class TimeoutError extends Error {
    constructor(timeoutMillis, description, timeoutType) {
        super(`[${timeoutType}] "${description}" timed out after ${timeoutMillis}ms`);
        this.timeoutMillis = timeoutMillis;
        this.description = description;
        this.timeoutType = timeoutType;
        Object.setPrototypeOf(this, TimeoutError.prototype);
    }
}
exports.TimeoutError = TimeoutError;
const startTimer = (timeoutMillis, description, timeoutType) => {
    let timeout;
    const timeoutError = new TimeoutError(timeoutMillis, description, timeoutType);
    const promise = new Promise((_resolve, reject) => {
        timeout = global.setTimeout(() => reject(timeoutError), timeoutMillis);
        timeout.unref();
    });
    return {
        timer: promise,
        cancelTimer: () => global.clearTimeout(timeout),
        errorMessage: timeoutError.message,
    };
};
async function filterAsync(array, callback) {
    const results = await Promise.allSettled(array.map(callback));
    (0, logUtils_1.logPromiseSettledRejections)(results);
    return array.filter((_, index) => {
        const result = results[index];
        return (0, pureUtil_1.isFulfilledPromise)(result) && result.value;
    });
}
function awaitResult(resultSupplier, timeoutMs) {
    const waitForResult = (0, async_retry_1.default)(resultSupplier, {
        retries: 3 + Math.ceil(timeoutMs / 1000),
        minTimeout: 200,
        maxTimeout: 1000,
    });
    return promiseWithTimeout(waitForResult, timeoutMs, 'awaitResult', 'polling timeout', false);
}
