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();
    }

    // ==================== 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_shouldThrow() {
        request.addHeader("X-API-Key", "wrong-key");

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

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

        // no session, should throw
        assertThatThrownBy(() -> interceptor.preHandle(request, response, null))
                .isInstanceOf(UnauthorizedException.class);
    }

    // ==================== 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_shouldThrow() {
        request.setParameter("apiKey", "wrong-key");

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

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

        // header should be used and pass
        boolean result = interceptor.preHandle(request, response, null);

        assertThat(result).isTrue();
    }

    // ==================== Session Fallback ====================

    @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);
        // no adminAccount set

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

    @Test
    void noSession_noApiKey_shouldThrow() {
        // no session created, no API key set

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

    @Test
    void nullSession_noApiKey_shouldThrow() {
        // explicitly test null session behavior
        // MockHttpServletRequest.getSession() returns non-null by default,
        // but we test invalid session attributes above which covers this path

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