package cn.novalon.gym.manage.gateway.filter;

import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.reactor.circuitbreaker.operator.CircuitBreakerOperator;
import io.github.resilience4j.reactor.retry.RetryOperator;
import io.github.resilience4j.reactor.timelimiter.TimeLimiterOperator;
import io.github.resilience4j.retry.Retry;
import io.github.resilience4j.timelimiter.TimeLimiter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

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

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

    private final CircuitBreaker circuitBreaker;
    private final Retry retry;
    private final TimeLimiter timeLimiter;

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

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

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

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

    public ResilienceFilter(CircuitBreaker circuitBreaker, Retry retry, TimeLimiter timeLimiter) {
        this.circuitBreaker = circuitBreaker;
        this.retry = retry;
        this.timeLimiter = timeLimiter;
        logger.info("ResilienceFilter initialized - CircuitBreaker: {}, Retry: {}, TimeLimiter: {}",
                circuitBreaker.getName(), retry.getName(), timeLimiter.getName());
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        if (!resilienceEnabled) {
            logger.debug("Resilience is disabled");
            return chain.filter(exchange);
        }

        logger.debug("Applying resilience patterns for request: {} {}", 
                exchange.getRequest().getMethod(), 
                exchange.getRequest().getPath());

        Mono<Void> chainMono = chain.filter(exchange);

        if (timeoutEnabled) {
            chainMono = chainMono.transform(TimeLimiterOperator.of(timeLimiter));
        }

        if (retryEnabled) {
            chainMono = chainMono.transform(RetryOperator.of(retry));
        }

        if (circuitBreakerEnabled) {
            chainMono = chainMono.transform(CircuitBreakerOperator.of(circuitBreaker));
        }

        return chainMono
                .onErrorResume(Exception.class, e -> handleFallback(exchange, e));
    }

    private Mono<Void> handleFallback(ServerWebExchange exchange, Throwable throwable) {
        logger.error("Fallback triggered for request: {} {} - Error: {}", 
                exchange.getRequest().getMethod(),
                exchange.getRequest().getPath(),
                throwable.getMessage());

        ServerHttpResponse response = exchange.getResponse();
        
        if (throwable instanceof io.github.resilience4j.circuitbreaker.CallNotPermittedException) {
            response.setStatusCode(HttpStatus.SERVICE_UNAVAILABLE);
            String errorMessage = "{\"error\":\"Service Unavailable\",\"code\":\"CIRCUIT_BREAKER_OPEN\"," +
                    "\"message\":\"Service is temporarily unavailable due to circuit breaker being open. " +
                    "Please try again later.\"}";
            return response.writeWith(Mono.just(response.bufferFactory().wrap(errorMessage.getBytes())));
        } else if (throwable instanceof java.util.concurrent.TimeoutException) {
            response.setStatusCode(HttpStatus.GATEWAY_TIMEOUT);
            String errorMessage = "{\"error\":\"Gateway Timeout\",\"code\":\"TIMEOUT\"," +
                    "\"message\":\"Request timed out. Please try again.\"}";
            return response.writeWith(Mono.just(response.bufferFactory().wrap(errorMessage.getBytes())));
        } else {
            response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
            String errorMessage = "{\"error\":\"Internal Server Error\",\"code\":\"INTERNAL_ERROR\"," +
                    "\"message\":\"An unexpected error occurred. Please try again later.\"}";
            return response.writeWith(Mono.just(response.bufferFactory().wrap(errorMessage.getBytes())));
        }
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE + 200;
    }
}
