package com.mosquito.project.exception;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mosquito.project.dto.ApiResponse;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.ServletWebRequest;

import java.util.HashMap;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;

/**
 * GlobalExceptionHandler测试 - 提升异常处理器覆盖率
 */
@ExtendWith(MockitoExtension.class)
class GlobalExceptionHandlerTest {

    private GlobalExceptionHandler exceptionHandler;
    private ObjectMapper objectMapper;
    
    @Mock
    private WebRequest webRequest;

    @BeforeEach
    void setUp() {
        exceptionHandler = new GlobalExceptionHandler();
        objectMapper = new ObjectMapper();
    }

    @Test
    @DisplayName("处理BusinessException - 基本情况")
    void handleBusinessException_Basic() throws Exception {
        // Setup
        BusinessException exception = new BusinessException("业务错误", HttpStatus.BAD_REQUEST, "BUSINESS_ERROR");
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setRequestURI("/api/test");
        when(webRequest.getDescription(false)).thenReturn("uri=/api/test");

        // Execute
        ResponseEntity<ApiResponse<Void>> response = exceptionHandler.handleBusinessException(exception, webRequest);

        // Verify
        assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
        ApiResponse<Void> apiResponse = response.getBody();
        assertNotNull(apiResponse);
        assertEquals(400, apiResponse.getCode());
        assertEquals("业务错误", apiResponse.getMessage());
        assertNotNull(apiResponse.getError());
        assertEquals("业务错误", apiResponse.getError().getMessage());
        assertEquals("BUSINESS_ERROR", apiResponse.getError().getCode());
        assertNotNull(apiResponse.getError().getDetails());
        @SuppressWarnings("unchecked")
        Map<String, Object> details = (Map<String, Object>) apiResponse.getError().getDetails();
        assertTrue(details.containsKey("code"));
        assertEquals("BUSINESS_ERROR", details.get("code"));
        assertEquals("/api/test", details.get("path"));
    }

    @Test
    @DisplayName("处理BusinessException - 带详细信息")
    void handleBusinessException_WithDetails() throws Exception {
        // Setup
        Map<String, Object> details = new HashMap<>();
        details.put("field", "username");
        details.put("reason", "duplicate");
        BusinessException exception = new BusinessException("验证失败", details);
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setRequestURI("/api/users");
        when(webRequest.getDescription(false)).thenReturn("uri=/api/users");

        // Execute
        ResponseEntity<ApiResponse<Void>> response = exceptionHandler.handleBusinessException(exception, webRequest);

        // Verify
        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
        ApiResponse<Void> apiResponse = response.getBody();
        assertNotNull(apiResponse);
        assertEquals(500, apiResponse.getCode());
        assertEquals("验证失败", apiResponse.getMessage());
        assertNotNull(apiResponse.getError());
        assertEquals("BUSINESS_ERROR", apiResponse.getError().getCode());
        @SuppressWarnings("unchecked")
        Map<String, Object> errorDetails = (Map<String, Object>) apiResponse.getError().getDetails();
        assertNotNull(errorDetails);
        assertEquals(4, errorDetails.size()); // code + path + field + reason
        assertEquals("BUSINESS_ERROR", errorDetails.get("code"));
        assertEquals("username", errorDetails.get("field"));
        assertEquals("duplicate", errorDetails.get("reason"));
        assertEquals("/api/users", errorDetails.get("path"));
    }

    @Test
    @DisplayName("处理ResourceNotFoundException")
    void handleResourceNotFoundException() throws Exception {
        // Setup
        ResourceNotFoundException exception = new ResourceNotFoundException("User", "12345");
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setRequestURI("/api/users/12345");
        when(webRequest.getDescription(false)).thenReturn("uri=/api/users/12345");

        // Execute
        ResponseEntity<ApiResponse<Void>> response = exceptionHandler.handleResourceNotFoundException(exception, webRequest);

        // Verify
        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
        ApiResponse<Void> apiResponse = response.getBody();
        assertNotNull(apiResponse);
        assertEquals(404, apiResponse.getCode());
        assertEquals("User not found with id: 12345", apiResponse.getMessage());
        assertNotNull(apiResponse.getError());
        @SuppressWarnings("unchecked")
        Map<String, Object> errorDetails = (Map<String, Object>) apiResponse.getError().getDetails();
        assertNotNull(errorDetails);
        assertEquals(3, errorDetails.size());
        assertEquals("User", errorDetails.get("resourceType"));
        assertEquals("12345", errorDetails.get("resourceId"));
        assertEquals("/api/users/12345", errorDetails.get("path"));
    }

    @Test
    @DisplayName("处理ValidationException - 带字段错误")
    void handleValidationException_WithFieldErrors() throws Exception {
        // Setup
        Map<String, String> errors = new HashMap<>();
        errors.put("username", "用户名不能为空");
        errors.put("email", "邮箱格式不正确");
        errors.put("age", "年龄必须大于0");
        ValidationException exception = new ValidationException("请求参数验证失败", errors);
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setRequestURI("/api/users");
        when(webRequest.getDescription(false)).thenReturn("uri=/api/users");

        // Execute
        ResponseEntity<ApiResponse<Void>> response = exceptionHandler.handleValidationException(exception, webRequest);

        // Verify
        assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
        ApiResponse<Void> apiResponse = response.getBody();
        assertNotNull(apiResponse);
        assertEquals(400, apiResponse.getCode());
        assertEquals("请求参数验证失败", apiResponse.getMessage());
        assertNotNull(apiResponse.getError());
        @SuppressWarnings("unchecked")
        Map<String, Object> errorDetails = (Map<String, Object>) apiResponse.getError().getDetails();
        assertNotNull(errorDetails);
        assertEquals(4, errorDetails.size());
        assertEquals("用户名不能为空", errorDetails.get("username"));
        assertEquals("邮箱格式不正确", errorDetails.get("email"));
        assertEquals("年龄必须大于0", errorDetails.get("age"));
        assertEquals("/api/users", errorDetails.get("path"));
    }

    @Test
    @DisplayName("处理ValidationException - 无字段错误")
    void handleValidationException_WithoutFieldErrors() throws Exception {
        // Setup
        ValidationException exception = new ValidationException("通用验证错误");
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setRequestURI("/api/test");
        when(webRequest.getDescription(false)).thenReturn("uri=/api/test");

        // Execute
        ResponseEntity<ApiResponse<Void>> response = exceptionHandler.handleValidationException(exception, webRequest);

        // Verify
        assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
        ApiResponse<Void> apiResponse = response.getBody();
        assertNotNull(apiResponse);
        assertEquals(400, apiResponse.getCode());
        assertEquals("通用验证错误", apiResponse.getMessage());
        assertNotNull(apiResponse.getError());
        @SuppressWarnings("unchecked")
        Map<String, Object> errorDetails = (Map<String, Object>) apiResponse.getError().getDetails();
        assertNotNull(errorDetails);
        assertEquals(1, errorDetails.size());
        assertEquals("/api/test", errorDetails.get("path"));
    }

    @Test
    @DisplayName("处理通用Exception")
    void handleGenericException() throws Exception {
        // Setup
        NullPointerException exception = new NullPointerException("Null pointer occurred");
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setRequestURI("/api/test");
        when(webRequest.getDescription(false)).thenReturn("uri=/api/test");

        // Execute
        ResponseEntity<ApiResponse<Void>> response = exceptionHandler.handleGenericException(exception, webRequest);

        // Verify
        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
        ApiResponse<Void> apiResponse = response.getBody();
        assertNotNull(apiResponse);
        assertEquals(500, apiResponse.getCode());
        assertEquals("An unexpected error occurred", apiResponse.getMessage());
        assertNotNull(apiResponse.getError());
        @SuppressWarnings("unchecked")
        Map<String, Object> errorDetails = (Map<String, Object>) apiResponse.getError().getDetails();
        assertNotNull(errorDetails);
        assertEquals(2, errorDetails.size());
        assertEquals("NullPointerException", errorDetails.get("exception"));
        assertEquals("/api/test", errorDetails.get("path"));
    }

    @Test
    @DisplayName("处理RuntimeException")
    void handleRuntimeException() throws Exception {
        // Setup
        IllegalArgumentException exception = new IllegalArgumentException("Invalid argument");
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setRequestURI("/api/validation");
        when(webRequest.getDescription(false)).thenReturn("uri=/api/validation");

        // Execute
        ResponseEntity<ApiResponse<Void>> response = exceptionHandler.handleGenericException(exception, webRequest);

        // Verify
        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
        ApiResponse<Void> apiResponse = response.getBody();
        assertNotNull(apiResponse);
        assertEquals(500, apiResponse.getCode());
        assertEquals("An unexpected error occurred", apiResponse.getMessage());
        assertNotNull(apiResponse.getError());
        @SuppressWarnings("unchecked")
        Map<String, Object> errorDetails = (Map<String, Object>) apiResponse.getError().getDetails();
        assertNotNull(errorDetails);
        assertEquals("IllegalArgumentException", errorDetails.get("exception"));
        assertEquals("/api/validation", errorDetails.get("path"));
    }

    @Test
    @DisplayName("ErrorResponse字段完整性测试")
    void testErrorResponseCompleteness() throws Exception {
        // Setup
        BusinessException exception = new BusinessException("测试错误", HttpStatus.BAD_REQUEST, "TEST_ERROR");
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setRequestURI("/api/test");
        when(webRequest.getDescription(false)).thenReturn("uri=/api/test");

        // Execute
        ResponseEntity<ApiResponse<Void>> response = exceptionHandler.handleBusinessException(exception, webRequest);

        // Verify response structure
        ApiResponse<Void> apiResponse = response.getBody();
        assertNotNull(apiResponse);
        assertNotNull(apiResponse.getTimestamp());
        assertNotNull(apiResponse.getCode());
        assertNotNull(apiResponse.getMessage());
        assertNotNull(apiResponse.getError());
        assertNotNull(apiResponse.getError().getDetails());
    }

    @Test
    @DisplayName("复杂URL路径处理测试")
    void testComplexUrlPathHandling() throws Exception {
        // Setup
        ResourceNotFoundException exception = new ResourceNotFoundException("Product", "PROD-001");
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setRequestURI("/api/v1/products/PROD-001/reviews");
        request.setQueryString("page=1&size=10");
        when(webRequest.getDescription(false)).thenReturn("uri=/api/v1/products/PROD-001/reviews");

        // Execute
        ResponseEntity<ApiResponse<Void>> response = exceptionHandler.handleResourceNotFoundException(exception, webRequest);

        // Verify
        ApiResponse<Void> apiResponse = response.getBody();
        assertNotNull(apiResponse);
        @SuppressWarnings("unchecked")
        Map<String, Object> errorDetails = (Map<String, Object>) apiResponse.getError().getDetails();
        assertEquals("/api/v1/products/PROD-001/reviews", errorDetails.get("path"));
        assertEquals("PROD-001", errorDetails.get("resourceId"));
        assertEquals("Product", errorDetails.get("resourceType"));
    }

    @Test
    @DisplayName("异常详细信息嵌套对象测试")
    void testNestedDetailsInException() throws Exception {
        // Setup
        Map<String, Object> nestedDetails = new HashMap<>();
        Map<String, String> fieldErrors = new HashMap<>();
        fieldErrors.put("username", "太短");
        fieldErrors.put("password", "太弱");
        nestedDetails.put("fieldErrors", fieldErrors);
        nestedDetails.put("validationLevel", "ERROR");
        
        BusinessException exception = new BusinessException("复杂验证错误", nestedDetails);
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setRequestURI("/api/register");
        when(webRequest.getDescription(false)).thenReturn("uri=/api/register");

        // Execute
        ResponseEntity<ApiResponse<Void>> response = exceptionHandler.handleBusinessException(exception, webRequest);

        // Verify
        ApiResponse<Void> apiResponse = response.getBody();
        assertNotNull(apiResponse);
        @SuppressWarnings("unchecked")
        Map<String, Object> errorDetails = (Map<String, Object>) apiResponse.getError().getDetails();
        assertNotNull(errorDetails);
        assertEquals(4, errorDetails.size()); // code + path + nested objects
        assertEquals("/api/register", errorDetails.get("path"));

        @SuppressWarnings("unchecked")
        Map<String, Object> fieldErrorsFromResponse = (Map<String, Object>) errorDetails.get("fieldErrors");
        assertNotNull(fieldErrorsFromResponse);
        assertEquals("太短", fieldErrorsFromResponse.get("username"));
        assertEquals("太弱", fieldErrorsFromResponse.get("password"));
        assertEquals("ERROR", errorDetails.get("validationLevel"));
    }
}
