package com.lingyue.parse.util;

import lombok.extern.slf4j.Slf4j;

import java.util.function.Supplier;

/**
 * 重试工具类
 * 支持带指数退避的重试机制
 * 
 * @author lingyue
 * @since 2026-01-14
 */
@Slf4j
public class RetryUtil {
    
    /**
     * 默认重试次数
     */
    private static final int DEFAULT_MAX_RETRIES = 3;
    
    /**
     * 默认初始延迟（毫秒）
     */
    private static final long DEFAULT_INITIAL_DELAY_MS = 1000;
    
    /**
     * 执行带重试的操作
     * 
     * @param operation 要执行的操作
     * @param operationName 操作名称（用于日志）
     * @return 操作结果
     */
    public static <T> T executeWithRetry(Supplier<T> operation, String operationName) {
        return executeWithRetry(operation, operationName, DEFAULT_MAX_RETRIES, DEFAULT_INITIAL_DELAY_MS);
    }
    
    /**
     * 执行带重试的操作
     * 
     * @param operation 要执行的操作
     * @param operationName 操作名称（用于日志）
     * @param maxRetries 最大重试次数
     * @param initialDelayMs 初始延迟（毫秒）
     * @return 操作结果
     */
    public static <T> T executeWithRetry(Supplier<T> operation, String operationName, 
                                         int maxRetries, long initialDelayMs) {
        int attempt = 0;
        Exception lastException = null;
        
        while (attempt <= maxRetries) {
            try {
                if (attempt > 0) {
                    log.info("重试 {} 操作，第 {} 次尝试", operationName, attempt);
                }
                
                return operation.get();
            } catch (Exception e) {
                lastException = e;
                attempt++;
                
                if (attempt > maxRetries) {
                    log.error("{} 操作失败，已达到最大重试次数 {}", operationName, maxRetries, e);
                    break;
                }
                
                // 指数退避：延迟时间 = initialDelayMs * 2^(attempt-1)
                long delayMs = initialDelayMs * (long) Math.pow(2, attempt - 1);
                log.warn("{} 操作失败，{} 毫秒后重试（第 {} 次尝试）: {}", 
                        operationName, delayMs, attempt, e.getMessage());
                
                try {
                    Thread.sleep(delayMs);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    log.error("重试延迟被中断", ie);
                    throw new RuntimeException("重试被中断", ie);
                }
            }
        }
        
        // 所有重试都失败，抛出最后一个异常
        throw new RuntimeException(operationName + " 操作失败，已重试 " + maxRetries + " 次", lastException);
    }
    
    /**
     * 执行带重试的操作（无返回值）
     * 
     * @param operation 要执行的操作
     * @param operationName 操作名称（用于日志）
     */
    public static void executeWithRetry(Runnable operation, String operationName) {
        executeWithRetry(operation, operationName, DEFAULT_MAX_RETRIES, DEFAULT_INITIAL_DELAY_MS);
    }
    
    /**
     * 执行带重试的操作（无返回值）
     * 
     * @param operation 要执行的操作
     * @param operationName 操作名称（用于日志）
     * @param maxRetries 最大重试次数
     * @param initialDelayMs 初始延迟（毫秒）
     */
    public static void executeWithRetry(Runnable operation, String operationName, 
                                       int maxRetries, long initialDelayMs) {
        executeWithRetry(() -> {
            operation.run();
            return null;
        }, operationName, maxRetries, initialDelayMs);
    }
}
