/**
 * 异步操作重试工具函数
 * @param {Function} asyncFn - 需要重试的异步函数
 * @param {Object} options - 重试配置选项
 * @param {number} options.maxRetries - 最大重试次数 (默认: 3)
 * @param {number} options.delay - 重试延迟时间(ms) (默认: 1000)
 * @param {string} options.backoffStrategy - 退避策略 ('fixed' | 'exponential' | 'linear') (默认: 'fixed')
 * @param {number} options.backoffFactor - 退避因子 (默认: 2)
 * @param {Function} options.shouldRetry - 自定义重试条件函数 (默认: 所有错误都重试)
 * @param {Function} options.onRetry - 重试回调函数
 * @param {number} options.timeout - 单次操作超时时间(ms)
 * @returns {Promise} 返回异步操作的结果
 */
export async function retryAsync(asyncFn, options = {}) {
    const {
        maxRetries = 3,
            delay = 1000,
            backoffStrategy = 'fixed',
            backoffFactor = 2,
            shouldRetry = () => true,
            onRetry = () => {},
            timeout
    } = options;

    let lastError;

    for (let attempt = 0; attempt <= maxRetries; attempt++) {
        try {
            // 如果设置了超时时间，包装函数添加超时控制
            const result = timeout ?
                await withTimeout(asyncFn, timeout) :
                await asyncFn();

            return result;
        } catch (error) {
            lastError = error;

            // 如果是最后一次尝试，直接抛出错误
            if (attempt === maxRetries) {
                throw error;
            }

            // 检查是否应该重试
            if (!shouldRetry(error, attempt + 1)) {
                throw error;
            }

            // 执行重试回调
            onRetry(error, attempt + 1, maxRetries);

            // 计算延迟时间并等待
            const waitTime = calculateDelay(delay, attempt, backoffStrategy, backoffFactor);
            await sleep(waitTime);
        }
    }

    throw lastError;
}

/**
 * 计算重试延迟时间
 */
function calculateDelay(baseDelay, attempt, strategy, factor) {
    switch (strategy) {
        case 'exponential':
            return baseDelay * Math.pow(factor, attempt);
        case 'linear':
            return baseDelay * (attempt + 1);
        case 'fixed':
        default:
            return baseDelay;
    }
}

/**
 * 延迟函数
 */
function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

/**
 * 为异步函数添加超时控制
 */
function withTimeout(asyncFn, timeoutMs) {
    return new Promise((resolve, reject) => {
        let isResolved = false;

        const timer = setTimeout(() => {
            if (!isResolved) {
                isResolved = true;
                reject(new Error(`Operation timed out after ${timeoutMs}ms`));
            }
        }, timeoutMs);

        asyncFn()
            .then(result => {
                if (!isResolved) {
                    isResolved = true;
                    clearTimeout(timer);
                    resolve(result);
                }
            })
            .catch(error => {
                if (!isResolved) {
                    isResolved = true;
                    clearTimeout(timer);
                    reject(error);
                }
            });
    });
}