package cn.novalon.manage.gateway.filter;

import cn.novalon.manage.gateway.service.PermissionService;
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.HttpStatus;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class RbacAuthorizationFilterTest {

        @Mock
        private GatewayFilterChain chain;

        @Mock
        private PermissionService permissionService;

        private RbacAuthorizationFilter filter;
        private ServerWebExchange exchange;

        @BeforeEach
        void setUp() {
                filter = new RbacAuthorizationFilter(permissionService);
        }

        @Test
        void testPublicPath_AllowAccess() {
                MockServerHttpRequest request = MockServerHttpRequest.get("/api/auth/login").build();
                exchange = MockServerWebExchange.from(request);

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

                Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
                                .filter(exchange, chain);

                StepVerifier.create(result)
                                .verifyComplete();

                verify(chain).filter(any(ServerWebExchange.class));
        }

        @Test
        void testPublicPath_Register() {
                MockServerHttpRequest request = MockServerHttpRequest.post("/api/auth/register").build();
                exchange = MockServerWebExchange.from(request);

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

                Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
                                .filter(exchange, chain);

                StepVerifier.create(result)
                                .verifyComplete();

                verify(chain).filter(any(ServerWebExchange.class));
        }

        @Test
        void testPublicPath_ActuatorHealth() {
                MockServerHttpRequest request = MockServerHttpRequest.get("/actuator/health").build();
                exchange = MockServerWebExchange.from(request);

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

                Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
                                .filter(exchange, chain);

                StepVerifier.create(result)
                                .verifyComplete();

                verify(chain).filter(any(ServerWebExchange.class));
        }

        @Test
        void testPublicPath_ActuatorInfo() {
                MockServerHttpRequest request = MockServerHttpRequest.get("/actuator/info").build();
                exchange = MockServerWebExchange.from(request);

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

                Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
                                .filter(exchange, chain);

                StepVerifier.create(result)
                                .verifyComplete();

                verify(chain).filter(any(ServerWebExchange.class));
        }

        @Test
        void testProtectedPath_NoUserId() {
                MockServerHttpRequest request = MockServerHttpRequest.get("/api/users").build();
                exchange = MockServerWebExchange.from(request);

                Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
                                .filter(exchange, chain);

                StepVerifier.create(result)
                                .verifyComplete();

                assert exchange.getResponse().getStatusCode() == HttpStatus.UNAUTHORIZED;
                verify(chain, never()).filter(any(ServerWebExchange.class));
        }

        @Test
        void testProtectedPath_WithUserId() {
                MockServerHttpRequest request = MockServerHttpRequest.get("/api/users")
                                .header("X-User-Id", "1")
                                .build();
                exchange = MockServerWebExchange.from(request);

                when(permissionService.hasPermission(eq(1L), eq("/api/users"), eq("GET"))).thenReturn(true);
                when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());

                Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
                                .filter(exchange, chain);

                StepVerifier.create(result)
                                .verifyComplete();

                verify(chain).filter(any(ServerWebExchange.class));
        }

        @Test
        void testProtectedPath_PostMethod() {
                MockServerHttpRequest request = MockServerHttpRequest.post("/api/users")
                                .header("X-User-Id", "1")
                                .build();
                exchange = MockServerWebExchange.from(request);

                when(permissionService.hasPermission(eq(1L), eq("/api/users"), eq("POST"))).thenReturn(true);
                when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());

                Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
                                .filter(exchange, chain);

                StepVerifier.create(result)
                                .verifyComplete();

                verify(chain).filter(any(ServerWebExchange.class));
        }

        @Test
        void testProtectedPath_PutMethod() {
                MockServerHttpRequest request = MockServerHttpRequest.put("/api/users/1")
                                .header("X-User-Id", "1")
                                .build();
                exchange = MockServerWebExchange.from(request);

                when(permissionService.hasPermission(eq(1L), eq("/api/users/1"), eq("PUT"))).thenReturn(true);
                when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());

                Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
                                .filter(exchange, chain);

                StepVerifier.create(result)
                                .verifyComplete();

                verify(chain).filter(any(ServerWebExchange.class));
        }

        @Test
        void testProtectedPath_DeleteMethod() {
                MockServerHttpRequest request = MockServerHttpRequest.delete("/api/users/1")
                                .header("X-User-Id", "1")
                                .build();
                exchange = MockServerWebExchange.from(request);

                when(permissionService.hasPermission(eq(1L), eq("/api/users/1"), eq("DELETE"))).thenReturn(true);
                when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());

                Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
                                .filter(exchange, chain);

                StepVerifier.create(result)
                                .verifyComplete();

                verify(chain).filter(any(ServerWebExchange.class));
        }

        @Test
        void testProtectedPath_EmptyUserId() {
                MockServerHttpRequest request = MockServerHttpRequest.get("/api/users")
                                .header("X-User-Id", "")
                                .build();
                exchange = MockServerWebExchange.from(request);

                Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
                                .filter(exchange, chain);

                StepVerifier.create(result)
                                .verifyComplete();

                assert exchange.getResponse().getStatusCode() == HttpStatus.UNAUTHORIZED;
                verify(chain, never()).filter(any(ServerWebExchange.class));
        }

        @Test
        void testMixedPath_AuthPath() {
                MockServerHttpRequest request = MockServerHttpRequest.get("/api/auth/logout").build();
                exchange = MockServerWebExchange.from(request);

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

                Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
                                .filter(exchange, chain);

                StepVerifier.create(result)
                                .verifyComplete();

                verify(chain).filter(any(ServerWebExchange.class));
        }

        @Test
        void testMixedPath_UserPath() {
                MockServerHttpRequest request = MockServerHttpRequest.get("/api/users/profile")
                                .header("X-User-Id", "1")
                                .build();
                exchange = MockServerWebExchange.from(request);

                when(permissionService.hasPermission(eq(1L), eq("/api/users/profile"), eq("GET"))).thenReturn(true);
                when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());

                Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
                                .filter(exchange, chain);

                StepVerifier.create(result)
                                .verifyComplete();

                verify(chain).filter(any(ServerWebExchange.class));
        }

        @Test
        void testActuatorPath_Metrics() {
                MockServerHttpRequest request = MockServerHttpRequest.get("/actuator/metrics")
                                .header("X-User-Id", "1")
                                .build();
                exchange = MockServerWebExchange.from(request);

                when(permissionService.hasPermission(eq(1L), eq("/actuator/metrics"), eq("GET"))).thenReturn(true);
                when(chain.filter(any(ServerWebExchange.class))).thenReturn(Mono.empty());

                Mono<Void> result = filter.apply(new RbacAuthorizationFilter.Config())
                                .filter(exchange, chain);

                StepVerifier.create(result)
                                .verifyComplete();

                verify(chain).filter(any(ServerWebExchange.class));
        }
}
