package cn.novalon.manage.gateway.filter;

import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import io.github.resilience4j.retry.Retry;
import io.github.resilience4j.retry.RetryConfig;
import io.github.resilience4j.timelimiter.TimeLimiter;
import io.github.resilience4j.timelimiter.TimeLimiterConfig;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.test.util.ReflectionTestUtils;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

import java.time.Duration;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

/**
 * ResilienceFilter单元测试
 * 
 * 文件定义：测试容错过滤器的核心功能
 * 涉及业务：断路器、重试、超时、降级
 * 
 * @author 张翔
 * @date 2026-03-26
 */
@ExtendWith(MockitoExtension.class)
class ResilienceFilterTest {

    @Mock
    private GatewayFilterChain chain;

    private CircuitBreaker circuitBreaker;
    private Retry retry;
    private TimeLimiter timeLimiter;
    private ResilienceFilter resilienceFilter;

    @BeforeEach
    void setUp() {
        CircuitBreakerConfig cbConfig = CircuitBreakerConfig.custom()
                .failureRateThreshold(50)
                .slidingWindowSize(100)
                .minimumNumberOfCalls(10)
                .waitDurationInOpenState(Duration.ofSeconds(10))
                .build();

        RetryConfig retryConfig = RetryConfig.custom()
                .maxAttempts(3)
                .waitDuration(Duration.ofMillis(500))
                .build();

        TimeLimiterConfig tlConfig = TimeLimiterConfig.custom()
                .timeoutDuration(Duration.ofSeconds(3))
                .build();

        circuitBreaker = CircuitBreaker.of("gateway", cbConfig);
        retry = Retry.of("gateway", retryConfig);
        timeLimiter = TimeLimiter.of("gateway", tlConfig);

        resilienceFilter = new ResilienceFilter(circuitBreaker, retry, timeLimiter);

        ReflectionTestUtils.setField(resilienceFilter, "resilienceEnabled", true);
        ReflectionTestUtils.setField(resilienceFilter, "circuitBreakerEnabled", true);
        ReflectionTestUtils.setField(resilienceFilter, "retryEnabled", true);
        ReflectionTestUtils.setField(resilienceFilter, "timeoutEnabled", true);
    }

    @Test
    void testFilter_WhenResilienceDisabled_ShouldContinueChain() {
        ReflectionTestUtils.setField(resilienceFilter, "resilienceEnabled", false);

        MockServerHttpRequest request = MockServerHttpRequest
                .method(HttpMethod.GET, "/api/users")
                .build();

        MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();

        when(chain.filter(any())).thenReturn(Mono.empty());

        StepVerifier.create(resilienceFilter.filter(exchange, chain))
                .verifyComplete();

        verify(chain).filter(exchange);
    }

    @Test
    void testFilter_WhenAllPatternsEnabled_ShouldApplyResilience() {
        MockServerHttpRequest request = MockServerHttpRequest
                .method(HttpMethod.GET, "/api/users")
                .build();

        MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();

        when(chain.filter(any())).thenReturn(Mono.empty());

        StepVerifier.create(resilienceFilter.filter(exchange, chain))
                .verifyComplete();

        verify(chain).filter(exchange);
    }

    @Test
    void testFilter_WhenCircuitBreakerDisabled_ShouldSkipCircuitBreaker() {
        ReflectionTestUtils.setField(resilienceFilter, "circuitBreakerEnabled", false);

        MockServerHttpRequest request = MockServerHttpRequest
                .method(HttpMethod.GET, "/api/users")
                .build();

        MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();

        when(chain.filter(any())).thenReturn(Mono.empty());

        StepVerifier.create(resilienceFilter.filter(exchange, chain))
                .verifyComplete();

        verify(chain).filter(exchange);
    }

    @Test
    void testFilter_WhenRetryDisabled_ShouldSkipRetry() {
        ReflectionTestUtils.setField(resilienceFilter, "retryEnabled", false);

        MockServerHttpRequest request = MockServerHttpRequest
                .method(HttpMethod.GET, "/api/users")
                .build();

        MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();

        when(chain.filter(any())).thenReturn(Mono.empty());

        StepVerifier.create(resilienceFilter.filter(exchange, chain))
                .verifyComplete();

        verify(chain).filter(exchange);
    }

    @Test
    void testFilter_WhenTimeoutDisabled_ShouldSkipTimeout() {
        ReflectionTestUtils.setField(resilienceFilter, "timeoutEnabled", false);

        MockServerHttpRequest request = MockServerHttpRequest
                .method(HttpMethod.GET, "/api/users")
                .build();

        MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();

        when(chain.filter(any())).thenReturn(Mono.empty());

        StepVerifier.create(resilienceFilter.filter(exchange, chain))
                .verifyComplete();

        verify(chain).filter(exchange);
    }

    @Test
    void testFilter_WhenChainThrowsException_ShouldHandleFallback() {
        MockServerHttpRequest request = MockServerHttpRequest
                .method(HttpMethod.GET, "/api/users")
                .build();

        MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();

        when(chain.filter(any())).thenReturn(Mono.error(new RuntimeException("Test error")));

        StepVerifier.create(resilienceFilter.filter(exchange, chain))
                .verifyComplete();

        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, exchange.getResponse().getStatusCode());
    }

    @Test
    void testGetOrder_ShouldReturnCorrectOrder() {
        int order = resilienceFilter.getOrder();

        assertEquals(-2147483448, order);
    }
}
