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

import cn.novalon.gym.manage.gateway.service.SignatureService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
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 static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

/**
 * SignatureFilter单元测试
 * 
 * 文件定义：测试签名验证过滤器的核心功能
 * 涉及业务：签名验证、白名单过滤、错误响应
 * 
 * @author 张翔
 * @date 2026-03-26
 */
@ExtendWith(MockitoExtension.class)
class SignatureFilterTest {

    @Mock
    private SignatureService signatureService;

    @Mock
    private GatewayFilterChain chain;

    @InjectMocks
    private SignatureFilter signatureFilter;

    private static final String TEST_SECRET = "TestSecretKey123";

    @BeforeEach
    void setUp() {
        ReflectionTestUtils.setField(signatureFilter, "signatureEnabled", true);
        ReflectionTestUtils.setField(signatureFilter, "signatureSecret", TEST_SECRET);
        ReflectionTestUtils.setField(signatureFilter, "whitelistPaths", "/actuator/health,/actuator/info");
    }

    @Test
    void testFilter_WhenSignatureDisabled_ShouldContinueChain() {
        ReflectionTestUtils.setField(signatureFilter, "signatureEnabled", 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(signatureFilter.filter(exchange, chain))
                .verifyComplete();

        verify(chain).filter(exchange);
        verify(signatureService, never()).verifySignature(any(), any());
    }

    @Test
    void testFilter_WhenPathIsWhitelisted_ShouldContinueChain() {
        MockServerHttpRequest request = MockServerHttpRequest
                .method(HttpMethod.GET, "/actuator/health")
                .build();

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

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

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

        verify(chain).filter(exchange);
        verify(signatureService, never()).verifySignature(any(), any());
    }

    @Test
    void testFilter_WhenSignatureValid_ShouldContinueChain() {
        MockServerHttpRequest request = MockServerHttpRequest
                .method(HttpMethod.GET, "/api/users")
                .header("X-Signature", "valid-signature")
                .header("X-Timestamp", String.valueOf(System.currentTimeMillis()))
                .header("X-Nonce", "test-nonce")
                .build();

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

        when(signatureService.verifySignature(any(), any())).thenReturn(true);
        when(chain.filter(any())).thenReturn(Mono.empty());

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

        verify(signatureService).verifySignature(request, TEST_SECRET);
        verify(chain).filter(exchange);
    }

    @Test
    void testFilter_WhenSignatureInvalid_ShouldReturnUnauthorized() {
        MockServerHttpRequest request = MockServerHttpRequest
                .method(HttpMethod.GET, "/api/users")
                .header("X-Signature", "invalid-signature")
                .header("X-Timestamp", String.valueOf(System.currentTimeMillis()))
                .header("X-Nonce", "test-nonce")
                .build();

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

        when(signatureService.verifySignature(any(), any())).thenReturn(false);

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

        verify(signatureService).verifySignature(request, TEST_SECRET);
        verify(chain, never()).filter(any());

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

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

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

        when(signatureService.verifySignature(any(), any())).thenReturn(false);

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

        verify(signatureService).verifySignature(request, TEST_SECRET);
        verify(chain, never()).filter(any());

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

    @Test
    void testFilter_WhenMultipleWhitelistPaths_ShouldMatchAny() {
        MockServerHttpRequest request1 = MockServerHttpRequest
                .method(HttpMethod.GET, "/actuator/health")
                .build();

        MockServerHttpRequest request2 = MockServerHttpRequest
                .method(HttpMethod.GET, "/actuator/info")
                .build();

        MockServerWebExchange exchange1 = MockServerWebExchange.builder(request1).build();
        MockServerWebExchange exchange2 = MockServerWebExchange.builder(request2).build();

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

        StepVerifier.create(signatureFilter.filter(exchange1, chain))
                .verifyComplete();

        StepVerifier.create(signatureFilter.filter(exchange2, chain))
                .verifyComplete();

        verify(chain, times(2)).filter(any());
        verify(signatureService, never()).verifySignature(any(), any());
    }

    @Test
    void testFilter_WhenPathStartsWithWhitelist_ShouldMatch() {
        MockServerHttpRequest request = MockServerHttpRequest
                .method(HttpMethod.GET, "/actuator/health/details")
                .build();

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

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

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

        verify(chain).filter(exchange);
        verify(signatureService, never()).verifySignature(any(), any());
    }

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

        assertEquals(-2147483498, order);
    }

    @Test
    void testFilter_WhenSignatureEnabled_ShouldVerifySignature() {
        MockServerHttpRequest request = MockServerHttpRequest
                .method(HttpMethod.GET, "/api/users")
                .header("X-Signature", "test-signature")
                .header("X-Timestamp", String.valueOf(System.currentTimeMillis()))
                .header("X-Nonce", "test-nonce")
                .build();

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

        when(signatureService.verifySignature(any(), any())).thenReturn(true);
        when(chain.filter(any())).thenReturn(Mono.empty());

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

        verify(signatureService).verifySignature(request, TEST_SECRET);
    }
}
