package cn.novalon.manage.gateway.config;

import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
import io.github.resilience4j.retry.Retry;
import io.github.resilience4j.retry.RetryConfig;
import io.github.resilience4j.retry.RetryRegistry;
import io.github.resilience4j.timelimiter.TimeLimiter;
import io.github.resilience4j.timelimiter.TimeLimiterConfig;
import io.github.resilience4j.timelimiter.TimeLimiterRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/**
 * Resilience4j配置类
 * 
 * 文件定义：配置断路器、重试、超时等容错机制
 * 涉及业务：网关容错增强，提高系统稳定性和可用性
 * 
 * 配置内容：
 * 1. CircuitBreaker：断路器模式，防止级联故障
 * 2. Retry：重试机制，处理临时故障
 * 3. TimeLimiter：超时控制，防止长时间阻塞
 * 4. Fallback：降级策略，提供备用响应
 * 
 * @author 张翔
 * @date 2026-03-26
 */
@Configuration
public class ResilienceConfig {

    private static final Logger logger = LoggerFactory.getLogger(ResilienceConfig.class);

    @Value("${resilience.circuit-breaker.enabled:true}")
    private boolean circuitBreakerEnabled;

    @Value("${resilience.circuit-breaker.failure-rate-threshold:50}")
    private float failureRateThreshold;

    @Value("${resilience.circuit-breaker.slow-call-rate-threshold:100}")
    private float slowCallRateThreshold;

    @Value("${resilience.circuit-breaker.slow-call-duration-threshold:2s}")
    private Duration slowCallDurationThreshold;

    @Value("${resilience.circuit-breaker.permitted-number-of-calls-in-half-open-state:10}")
    private int permittedNumberOfCallsInHalfOpenState;

    @Value("${resilience.circuit-breaker.sliding-window-type:COUNT_BASED}")
    private String slidingWindowType;

    @Value("${resilience.circuit-breaker.sliding-window-size:100}")
    private int slidingWindowSize;

    @Value("${resilience.circuit-breaker.minimum-number-of-calls:10}")
    private int minimumNumberOfCalls;

    @Value("${resilience.circuit-breaker.wait-duration-in-open-state:10s}")
    private Duration waitDurationInOpenState;

    @Value("${resilience.retry.enabled:true}")
    private boolean retryEnabled;

    @Value("${resilience.retry.max-attempts:3}")
    private int retryMaxAttempts;

    @Value("${resilience.retry.wait-duration:500ms}")
    private Duration retryWaitDuration;

    @Value("${resilience.retry.exponential-backoff-multiplier:2}")
    private double exponentialBackoffMultiplier;

    @Value("${resilience.timeout.enabled:true}")
    private boolean timeoutEnabled;

    @Value("${resilience.timeout.duration:3s}")
    private Duration timeoutDuration;

    @Bean
    public CircuitBreakerRegistry circuitBreakerRegistry() {
        CircuitBreakerConfig config = CircuitBreakerConfig.custom()
                .failureRateThreshold(failureRateThreshold)
                .slowCallRateThreshold(slowCallRateThreshold)
                .slowCallDurationThreshold(slowCallDurationThreshold)
                .permittedNumberOfCallsInHalfOpenState(permittedNumberOfCallsInHalfOpenState)
                .slidingWindowType(CircuitBreakerConfig.SlidingWindowType.valueOf(slidingWindowType))
                .slidingWindowSize(slidingWindowSize)
                .minimumNumberOfCalls(minimumNumberOfCalls)
                .waitDurationInOpenState(waitDurationInOpenState)
                .recordExceptions(Exception.class)
                .ignoreExceptions(IllegalArgumentException.class)
                .build();

        Map<String, CircuitBreakerConfig> configs = new HashMap<>();
        configs.put("default", config);

        CircuitBreakerRegistry registry = CircuitBreakerRegistry.of(configs);

        logger.info("CircuitBreaker registry initialized with {} configurations", configs.size());
        logger.info("Failure rate threshold: {}%", failureRateThreshold);
        logger.info("Slow call duration threshold: {}", slowCallDurationThreshold);
        logger.info("Sliding window size: {}", slidingWindowSize);
        logger.info("Wait duration in open state: {}", waitDurationInOpenState);

        return registry;
    }

    @Bean
    public CircuitBreaker gatewayCircuitBreaker(CircuitBreakerRegistry registry) {
        CircuitBreaker circuitBreaker = registry.circuitBreaker("gateway", "default");

        circuitBreaker.getEventPublisher()
                .onStateTransition(event -> 
                    logger.warn("CircuitBreaker state transition: {} -> {} for {}", 
                            event.getStateTransition().getFromState(),
                            event.getStateTransition().getToState(),
                            event.getCircuitBreakerName()))
                .onError(event -> 
                    logger.error("CircuitBreaker error: {} - {}", 
                            event.getCircuitBreakerName(),
                            event.getThrowable().getMessage()))
                .onSuccess(event -> 
                    logger.debug("CircuitBreaker success: {} - Duration: {}ms", 
                            event.getCircuitBreakerName(),
                            event.getElapsedDuration().toMillis()));

        logger.info("Gateway CircuitBreaker created: {}", circuitBreaker.getName());
        return circuitBreaker;
    }

    @Bean
    public RetryRegistry retryRegistry() {
        RetryConfig config = RetryConfig.custom()
                .maxAttempts(retryMaxAttempts)
                .waitDuration(retryWaitDuration)
                .retryExceptions(Exception.class)
                .ignoreExceptions(IllegalArgumentException.class)
                .build();

        Map<String, RetryConfig> configs = new HashMap<>();
        configs.put("default", config);

        RetryRegistry registry = RetryRegistry.of(configs);

        logger.info("Retry registry initialized with {} configurations", configs.size());
        logger.info("Max attempts: {}", retryMaxAttempts);
        logger.info("Wait duration: {}", retryWaitDuration);

        return registry;
    }

    @Bean
    public Retry gatewayRetry(RetryRegistry registry) {
        Retry retry = registry.retry("gateway", "default");

        retry.getEventPublisher()
                .onRetry(event -> 
                    logger.warn("Retry attempt {} of {} for {}", 
                            event.getNumberOfRetryAttempts(),
                            retryMaxAttempts,
                            event.getName()))
                .onError(event -> 
                    logger.error("Retry failed after {} attempts for {}", 
                            event.getNumberOfRetryAttempts(),
                            event.getName()))
                .onSuccess(event -> 
                    logger.debug("Retry succeeded after {} attempts for {}", 
                            event.getNumberOfRetryAttempts(),
                            event.getName()));

        logger.info("Gateway Retry created: {}", retry.getName());
        return retry;
    }

    @Bean
    public TimeLimiterRegistry timeLimiterRegistry() {
        TimeLimiterConfig config = TimeLimiterConfig.custom()
                .timeoutDuration(timeoutDuration)
                .cancelRunningFuture(true)
                .build();

        Map<String, TimeLimiterConfig> configs = new HashMap<>();
        configs.put("default", config);

        TimeLimiterRegistry registry = TimeLimiterRegistry.of(configs);

        logger.info("TimeLimiter registry initialized with {} configurations", configs.size());
        logger.info("Timeout duration: {}", timeoutDuration);

        return registry;
    }

    @Bean
    public TimeLimiter gatewayTimeLimiter(TimeLimiterRegistry registry) {
        TimeLimiter timeLimiter = registry.timeLimiter("gateway", "default");

        timeLimiter.getEventPublisher()
                .onTimeout(event -> 
                    logger.warn("Timeout occurred for {}", 
                            event.getTimeLimiterName()))
                .onSuccess(event -> 
                    logger.debug("TimeLimiter success for {}", 
                            event.getTimeLimiterName()));

        logger.info("Gateway TimeLimiter created: {}", timeLimiter.getName());
        return timeLimiter;
    }
}
