package cn.novalon.manage.sys.filter;

import io.github.resilience4j.ratelimiter.RateLimiter;
import io.github.resilience4j.ratelimiter.RateLimiterConfig;
import io.github.resilience4j.ratelimiter.RateLimiterRegistry;
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.http.HttpStatus;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

import java.net.InetSocketAddress;
import java.time.Duration;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class RateLimitFilterTest {

    @Mock
    private RateLimiterRegistry rateLimiterRegistry;

    @Mock
    private RateLimiter rateLimiter;

    @Mock
    private WebFilterChain webFilterChain;

    private RateLimitFilter rateLimitFilter;
    private MockServerWebExchange exchange;

    @BeforeEach
    void setUp() {
        when(rateLimiterRegistry.rateLimiter("apiRateLimiter")).thenReturn(rateLimiter);
        
        rateLimitFilter = new RateLimitFilter(rateLimiterRegistry);
        
        exchange = MockServerWebExchange.from(
                org.springframework.mock.http.server.reactive.MockServerHttpRequest.get("/api/test")
                        .remoteAddress(new InetSocketAddress("192.168.1.1", 8080))
                        .build()
        );
    }

    @Test
    void testFilter_WithPermissionGranted() {
        when(rateLimiter.acquirePermission()).thenReturn(true);
        when(webFilterChain.filter(any())).thenReturn(Mono.empty());
        
        Mono<Void> result = rateLimitFilter.filter(exchange, webFilterChain);
        
        StepVerifier.create(result)
                .verifyComplete();
        
        verify(webFilterChain).filter(any());
    }

    @Test
    void testFilter_WithPermissionDenied() {
        RateLimiterConfig config = RateLimiterConfig.custom()
                .limitForPeriod(100)
                .limitRefreshPeriod(Duration.ofSeconds(1))
                .build();
        when(rateLimiter.getRateLimiterConfig()).thenReturn(config);
        when(rateLimiter.acquirePermission()).thenReturn(false);
        
        Mono<Void> result = rateLimitFilter.filter(exchange, webFilterChain);
        
        StepVerifier.create(result)
                .verifyComplete();
        
        assertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.TOO_MANY_REQUESTS);
        assertThat(exchange.getResponse().getHeaders().getFirst("X-RateLimit-Limit")).isEqualTo("100");
        assertThat(exchange.getResponse().getHeaders().getFirst("X-RateLimit-Remaining")).isEqualTo("0");
        assertThat(exchange.getResponse().getHeaders().getFirst("Retry-After")).isEqualTo("1");
    }

    @Test
    void testFilter_WithXForwardedForHeader() {
        when(rateLimiter.acquirePermission()).thenReturn(true);
        when(webFilterChain.filter(any())).thenReturn(Mono.empty());
        
        MockServerWebExchange exchangeWithHeader = MockServerWebExchange.from(
                org.springframework.mock.http.server.reactive.MockServerHttpRequest.get("/api/test")
                        .header("X-Forwarded-For", "10.0.0.1")
                        .build()
        );
        
        Mono<Void> result = rateLimitFilter.filter(exchangeWithHeader, webFilterChain);
        
        StepVerifier.create(result)
                .verifyComplete();
        
        verify(webFilterChain).filter(any());
    }

    @Test
    void testFilter_WithXRealIPHeader() {
        when(rateLimiter.acquirePermission()).thenReturn(true);
        when(webFilterChain.filter(any())).thenReturn(Mono.empty());
        
        MockServerWebExchange exchangeWithHeader = MockServerWebExchange.from(
                org.springframework.mock.http.server.reactive.MockServerHttpRequest.get("/api/test")
                        .header("X-Real-IP", "10.0.0.2")
                        .build()
        );
        
        Mono<Void> result = rateLimitFilter.filter(exchangeWithHeader, webFilterChain);
        
        StepVerifier.create(result)
                .verifyComplete();
        
        verify(webFilterChain).filter(any());
    }

    @Test
    void testFilter_WithUnknownIP() {
        when(rateLimiter.acquirePermission()).thenReturn(true);
        when(webFilterChain.filter(any())).thenReturn(Mono.empty());
        
        MockServerWebExchange exchangeWithUnknownIP = MockServerWebExchange.from(
                org.springframework.mock.http.server.reactive.MockServerHttpRequest.get("/api/test")
                        .header("X-Forwarded-For", "unknown")
                        .build()
        );
        
        Mono<Void> result = rateLimitFilter.filter(exchangeWithUnknownIP, webFilterChain);
        
        StepVerifier.create(result)
                .verifyComplete();
        
        verify(webFilterChain).filter(any());
    }

    @Test
    void testFilter_WithEmptyIP() {
        when(rateLimiter.acquirePermission()).thenReturn(true);
        when(webFilterChain.filter(any())).thenReturn(Mono.empty());
        
        MockServerWebExchange exchangeWithEmptyIP = MockServerWebExchange.from(
                org.springframework.mock.http.server.reactive.MockServerHttpRequest.get("/api/test")
                        .header("X-Forwarded-For", "")
                        .build()
        );
        
        Mono<Void> result = rateLimitFilter.filter(exchangeWithEmptyIP, webFilterChain);
        
        StepVerifier.create(result)
                .verifyComplete();
        
        verify(webFilterChain).filter(any());
    }

    @Test
    void testFilter_WithNullRemoteAddress() {
        when(rateLimiter.acquirePermission()).thenReturn(true);
        when(webFilterChain.filter(any())).thenReturn(Mono.empty());
        
        MockServerWebExchange exchangeWithNullAddress = MockServerWebExchange.from(
                org.springframework.mock.http.server.reactive.MockServerHttpRequest.get("/api/test")
                        .header("X-Forwarded-For", "unknown")
                        .header("X-Real-IP", "unknown")
                        .build()
        );
        
        Mono<Void> result = rateLimitFilter.filter(exchangeWithNullAddress, webFilterChain);
        
        StepVerifier.create(result)
                .verifyComplete();
        
        verify(webFilterChain).filter(any());
    }
}