package dev.rheinsw.gateway.filter;

import dev.rheinsw.gateway.service.ApiKeyValidator;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
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 static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

class ApiKeyGatewayFilterTest {

    @Mock
    private ApiKeyValidator apiKeyValidator;

    @Mock
    private GatewayFilterChain chain;

    private ApiKeyGatewayFilter filter;

    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
        filter = new ApiKeyGatewayFilter(apiKeyValidator);

        // Inject expectedFrontendOrigin manually
        ReflectionTestUtils.setField(filter, "expectedFrontendOrigin", "https://localhost:3000");
    }

    private void injectMockRoute(MockServerWebExchange exchange, String routeId) {
        Route mockRoute = mock(Route.class);
        when(mockRoute.getId()).thenReturn(routeId);
        exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR, mockRoute);
    }

    @Test
    void shouldAllowPublicRequestWithoutValidation() {
        MockServerHttpRequest request = MockServerHttpRequest.get("/api/public/test").build();
        MockServerWebExchange exchange = MockServerWebExchange.from(request);

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

        filter.filter(exchange, chain).block();

        verify(chain, times(1)).filter(exchange);
    }

    @Test
    void shouldAllowValidFrontendApiKey() {
        MockServerHttpRequest request = MockServerHttpRequest.get("/api/contact/test")
                .header("Origin", "https://localhost:3000")
                .header("X-Frontend-Key", "valid-frontend-key")
                .build();
        MockServerWebExchange exchange = MockServerWebExchange.from(request);
        injectMockRoute(exchange, "contactService");

        when(apiKeyValidator.isAuthorized(eq("valid-frontend-key"), eq("contactService"), eq(true))).thenReturn(true);
        when(chain.filter(exchange)).thenReturn(Mono.empty());

        filter.filter(exchange, chain).block();

        verify(apiKeyValidator).isAuthorized("valid-frontend-key", "contactService", true);
        verify(chain, times(1)).filter(exchange);
    }

    @Test
    void shouldRejectInvalidFrontendApiKey() {
        MockServerHttpRequest request = MockServerHttpRequest.get("/api/contact/test")
                .header("Origin", "https://localhost:3000")
                .header("X-Frontend-Key", "invalid-frontend-key")
                .build();
        MockServerWebExchange exchange = MockServerWebExchange.from(request);
        injectMockRoute(exchange, "contactService");

        when(apiKeyValidator.isAuthorized(eq("invalid-frontend-key"), eq("contactService"), eq(true))).thenReturn(false);

        filter.filter(exchange, chain).block();

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

    @Test
    void shouldAllowValidInternalApiKey() {
        MockServerHttpRequest request = MockServerHttpRequest.get("/api/contact/test")
                .header("X-Internal-Auth", "valid-internal-key")
                .build();
        MockServerWebExchange exchange = MockServerWebExchange.from(request);
        injectMockRoute(exchange, "contactService");

        when(apiKeyValidator.isAuthorized(eq("valid-internal-key"), eq("contactService"), eq(false))).thenReturn(true);
        when(chain.filter(exchange)).thenReturn(Mono.empty());

        filter.filter(exchange, chain).block();

        verify(apiKeyValidator).isAuthorized("valid-internal-key", "contactService", false);
        verify(chain, times(1)).filter(exchange);
    }

    @Test
    void shouldRejectInvalidInternalApiKey() {
        MockServerHttpRequest request = MockServerHttpRequest.get("/api/contact/test")
                .header("X-Internal-Auth", "invalid-internal-key")
                .build();
        MockServerWebExchange exchange = MockServerWebExchange.from(request);
        injectMockRoute(exchange, "contactService");

        when(apiKeyValidator.isAuthorized(eq("invalid-internal-key"), eq("contactService"), eq(false))).thenReturn(false);

        filter.filter(exchange, chain).block();

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