package com.linearpast.minecraftmanager.interceptor;

import com.linearpast.minecraftmanager.entity.Operators;
import com.linearpast.minecraftmanager.exception.UnauthorizedException;
import jakarta.servlet.http.HttpSession;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.util.ReflectionTestUtils;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

class ApiKeyInterceptorTest {

    private static final String VALID_API_KEY = "test-api-key-123";

    private ApiKeyInterceptor interceptor;
    private MockHttpServletRequest request;
    private MockHttpServletResponse response;

    @BeforeEach
    void setUp() {
        interceptor = new ApiKeyInterceptor();
        ReflectionTestUtils.setField(interceptor, "apiKey", VALID_API_KEY);
        request = new MockHttpServletRequest();
        response = new MockHttpServletResponse();
    }

    // ==================== X-API-Key Header ====================

    @Test
    void validApiKeyInHeader_shouldPass() {
        request.addHeader("X-API-Key", VALID_API_KEY);

        boolean result = interceptor.preHandle(request, response, null);

        assertThat(result).isTrue();
    }

    @Test
    void invalidApiKeyInHeader_shouldReturnJson403() {
        request.addHeader("X-API-Key", "wrong-key");

        boolean result = interceptor.preHandle(request, response, null);

        assertThat(result).isFalse();
        assertThat(response.getStatus()).isEqualTo(403);
        assertThat(response.getContentType()).startsWith("application/json");
    }

    // ==================== X-API-TOKEN Header ====================

    @Test
    void validApiTokenHeader_shouldPass() {
        request.addHeader("X-API-TOKEN", VALID_API_KEY);

        boolean result = interceptor.preHandle(request, response, null);

        assertThat(result).isTrue();
    }

    @Test
    void invalidApiTokenHeader_shouldReturnJson403() {
        request.addHeader("X-API-TOKEN", "wrong-token");

        boolean result = interceptor.preHandle(request, response, null);

        assertThat(result).isFalse();
        assertThat(response.getStatus()).isEqualTo(403);
    }

    // ==================== API Key Query Param ====================

    @Test
    void validApiKeyInQueryParam_shouldPass() {
        request.setParameter("apiKey", VALID_API_KEY);

        boolean result = interceptor.preHandle(request, response, null);

        assertThat(result).isTrue();
    }

    @Test
    void invalidApiKeyInQueryParam_shouldReturnJson403() {
        request.setParameter("apiKey", "wrong-key");

        boolean result = interceptor.preHandle(request, response, null);

        assertThat(result).isFalse();
        assertThat(response.getStatus()).isEqualTo(403);
    }

    // ==================== Priority ====================

    @Test
    void apiKeyHeaderTakesPriorityOverTokenHeader() {
        request.addHeader("X-API-Key", VALID_API_KEY);
        request.addHeader("X-API-TOKEN", "wrong-token");

        boolean result = interceptor.preHandle(request, response, null);

        assertThat(result).isTrue();
    }

    @Test
    void headerTakesPriorityOverQueryParam() {
        request.addHeader("X-API-Key", VALID_API_KEY);
        request.setParameter("apiKey", "wrong-key");

        boolean result = interceptor.preHandle(request, response, null);

        assertThat(result).isTrue();
    }

    // ==================== API URI path ====================

    @Test
    void requestToApiPath_withoutAuth_shouldReturnJson403() {
        request.setRequestURI("/api/whitelist/list");

        boolean result = interceptor.preHandle(request, response, null);

        assertThat(result).isFalse();
        assertThat(response.getStatus()).isEqualTo(403);
        assertThat(response.getContentType()).startsWith("application/json");
    }

    // ==================== Session Fallback (browser) ====================

    @Test
    void validAdminSession_shouldPass() {
        HttpSession session = request.getSession(true);
        session.setAttribute("isLoggedIn", true);
        session.setAttribute("adminAccount", new Operators());

        boolean result = interceptor.preHandle(request, response, null);

        assertThat(result).isTrue();
    }

    @Test
    void sessionNotLoggedIn_shouldThrow() {
        HttpSession session = request.getSession(true);
        session.setAttribute("isLoggedIn", false);
        session.setAttribute("adminAccount", new Operators());

        assertThatThrownBy(() -> interceptor.preHandle(request, response, null))
                .isInstanceOf(UnauthorizedException.class);
    }

    @Test
    void sessionNoAdminAccount_shouldThrow() {
        HttpSession session = request.getSession(true);
        session.setAttribute("isLoggedIn", true);

        assertThatThrownBy(() -> interceptor.preHandle(request, response, null))
                .isInstanceOf(UnauthorizedException.class);
    }

    @Test
    void noSession_noApiKey_shouldThrow() {
        assertThatThrownBy(() -> interceptor.preHandle(request, response, null))
                .isInstanceOf(UnauthorizedException.class);
    }
}
