package com.mosquito.project.web;

import com.mosquito.project.persistence.entity.ApiKeyEntity;
import com.mosquito.project.persistence.repository.ApiKeyRepository;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.time.OffsetDateTime;
import java.util.Base64;
import java.util.Optional;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
@DisplayName("ApiKeyAuthInterceptor 测试")
class ApiKeyAuthInterceptorTest {

    @Mock
    private ApiKeyRepository apiKeyRepository;

    @Mock
    private HttpServletRequest request;

    @Mock
    private HttpServletResponse response;

    @Mock
    private Object handler;

    private ApiKeyAuthInterceptor interceptor;

    @BeforeEach
    void setUp() {
        interceptor = new ApiKeyAuthInterceptor(apiKeyRepository);
    }

    @Test
    @DisplayName("应该拒绝null API Key")
    void shouldRejectNullApiKey() {
        // Given
        when(request.getHeader("X-API-Key")).thenReturn(null);

        // When
        boolean result = interceptor.preHandle(request, response, handler);

        // Then
        assertThat(result).isFalse();
        verify(response).setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        verify(apiKeyRepository, never()).findByKeyPrefix(anyString());
    }

    @Test
    @DisplayName("应该拒绝空白API Key")
    void shouldRejectBlankApiKey() {
        // Given
        when(request.getHeader("X-API-Key")).thenReturn("   ");

        // When
        boolean result = interceptor.preHandle(request, response, handler);

        // Then
        assertThat(result).isFalse();
        verify(response).setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    }

    @Test
    @DisplayName("应该拒绝不存在的API Key前缀")
    void shouldRejectNonExistentKeyPrefix() {
        // Given
        when(request.getHeader("X-API-Key")).thenReturn("test-api-key-12345");
        when(apiKeyRepository.findByKeyPrefix(anyString())).thenReturn(Optional.empty());

        // When
        boolean result = interceptor.preHandle(request, response, handler);

        // Then
        assertThat(result).isFalse();
        verify(response).setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    }

    @Test
    @DisplayName("应该拒绝已吊销的API Key")
    void shouldRejectRevokedApiKey() {
        // Given
        String apiKey = "test-api-key-12345";
        ApiKeyEntity entity = new ApiKeyEntity();
        entity.setRevokedAt(OffsetDateTime.now());

        when(request.getHeader("X-API-Key")).thenReturn(apiKey);
        when(apiKeyRepository.findByKeyPrefix(anyString())).thenReturn(Optional.of(entity));

        // When
        boolean result = interceptor.preHandle(request, response, handler);

        // Then
        assertThat(result).isFalse();
        verify(response).setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    }

    @Test
    @DisplayName("应该拒绝哈希不匹配的API Key")
    void shouldRejectMismatchedApiKeyHash() throws Exception {
        // Given
        String apiKey = "test-api-key-12345";
        byte[] salt = new byte[16];
        String saltBase64 = Base64.getEncoder().encodeToString(salt);

        ApiKeyEntity entity = new ApiKeyEntity();
        entity.setSalt(saltBase64);
        entity.setKeyHash("wrong-hash");
        entity.setRevokedAt(null);

        when(request.getHeader("X-API-Key")).thenReturn(apiKey);
        when(apiKeyRepository.findByKeyPrefix(anyString())).thenReturn(Optional.of(entity));

        // When
        boolean result = interceptor.preHandle(request, response, handler);

        // Then
        assertThat(result).isFalse();
        verify(response).setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    }

    @Test
    @DisplayName("应该接受有效的API Key")
    void shouldAcceptValidApiKey() throws Exception {
        // Given
        String apiKey = "test-api-key-12345";
        byte[] salt = new byte[16];
        String saltBase64 = Base64.getEncoder().encodeToString(salt);

        // 计算正确的哈希
        javax.crypto.SecretKeyFactory skf = javax.crypto.SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        javax.crypto.spec.PBEKeySpec spec = new javax.crypto.spec.PBEKeySpec(apiKey.toCharArray(), salt, 185000, 256);
        byte[] derived = skf.generateSecret(spec).getEncoded();
        String correctHash = Base64.getEncoder().encodeToString(derived);

        ApiKeyEntity entity = new ApiKeyEntity();
        entity.setSalt(saltBase64);
        entity.setKeyHash(correctHash);
        entity.setRevokedAt(null);

        when(request.getHeader("X-API-Key")).thenReturn(apiKey);
        when(apiKeyRepository.findByKeyPrefix(anyString())).thenReturn(Optional.of(entity));

        // When
        boolean result = interceptor.preHandle(request, response, handler);

        // Then
        assertThat(result).isTrue();
        verify(response, never()).setStatus(anyInt());
        verify(request).setAttribute(eq("apiKeyPrefix"), anyString());
    }

    @Test
    @DisplayName("应该处理短API Key")
    void shouldHandleShortApiKey() {
        // Given
        String shortKey = "short";
        when(request.getHeader("X-API-Key")).thenReturn(shortKey);
        when(apiKeyRepository.findByKeyPrefix(anyString())).thenReturn(Optional.empty());

        // When
        boolean result = interceptor.preHandle(request, response, handler);

        // Then
        assertThat(result).isFalse();
        verify(apiKeyRepository).findByKeyPrefix("short");
    }

    @Test
    @DisplayName("应该处理加密异常")
    void shouldHandleCryptoException() {
        // Given
        String apiKey = "test-api-key-12345";

        ApiKeyEntity entity = new ApiKeyEntity();
        entity.setSalt("invalid-base64!!!"); // 无效的Base64会导致异常
        entity.setKeyHash("some-hash");
        entity.setRevokedAt(null);

        when(request.getHeader("X-API-Key")).thenReturn(apiKey);
        when(apiKeyRepository.findByKeyPrefix(anyString())).thenReturn(Optional.of(entity));

        // When
        boolean result = interceptor.preHandle(request, response, handler);

        // Then
        assertThat(result).isFalse();
        verify(response).setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    }

    @Test
    @DisplayName("应该正确提取API Key前缀")
    void shouldExtractCorrectPrefix() {
        // Given
        String apiKey = "abcdefghijklmnopqrstuvwxyz";
        when(request.getHeader("X-API-Key")).thenReturn(apiKey);
        when(apiKeyRepository.findByKeyPrefix("abcdefghijkl")).thenReturn(Optional.empty());

        // When
        interceptor.preHandle(request, response, handler);

        // Then
        verify(apiKeyRepository).findByKeyPrefix("abcdefghijkl");
    }

    @Test
    @DisplayName("应该处理带空格的API Key")
    void shouldHandleApiKeyWithSpaces() {
        // Given
        String apiKey = "  test-key-123  ";
        when(request.getHeader("X-API-Key")).thenReturn(apiKey);
        when(apiKeyRepository.findByKeyPrefix(anyString())).thenReturn(Optional.empty());

        // When
        boolean result = interceptor.preHandle(request, response, handler);

        // Then
        assertThat(result).isFalse();
        // 前缀应该被trim
        verify(apiKeyRepository).findByKeyPrefix(contains("test-key"));
    }
}
