package cn.novalon.gym.manage.sys.util;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.http.HttpHeaders;

import java.net.InetSocketAddress;
import java.util.Optional;

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

/**
 * IpUtils 单元测试
 * 
 * @author 张翔
 * @date 2026-04-03
 */
class IpUtilsTest {

    @Test
    @DisplayName("当request为null时,应返回unknown")
    void getClientIp_whenRequestIsNull_shouldReturnUnknown() {
        String ip = IpUtils.getClientIp((ServerRequest) null);
        assertEquals("unknown", ip);
    }

    @Test
    @DisplayName("当X-Forwarded-For头存在时,应返回第一个IP")
    void getClientIp_whenXForwardedForExists_shouldReturnFirstIp() {
        ServerRequest request = mock(ServerRequest.class);
        ServerRequest.Headers headers = mock(ServerRequest.Headers.class);
        
        when(request.headers()).thenReturn(headers);
        when(headers.firstHeader("X-Forwarded-For")).thenReturn("192.168.1.100, 10.0.0.1");
        when(request.remoteAddress()).thenReturn(Optional.empty());
        
        String ip = IpUtils.getClientIp(request);
        assertEquals("192.168.1.100", ip);
    }

    @Test
    @DisplayName("当X-Forwarded-For为单个IP时,应直接返回")
    void getClientIp_whenXForwardedForSingleIp_shouldReturnIt() {
        ServerRequest request = mock(ServerRequest.class);
        ServerRequest.Headers headers = mock(ServerRequest.Headers.class);
        
        when(request.headers()).thenReturn(headers);
        when(headers.firstHeader("X-Forwarded-For")).thenReturn("192.168.1.100");
        when(request.remoteAddress()).thenReturn(Optional.empty());
        
        String ip = IpUtils.getClientIp(request);
        assertEquals("192.168.1.100", ip);
    }

    @Test
    @DisplayName("当X-Forwarded-For为unknown时,应检查X-Real-IP")
    void getClientIp_whenXForwardedForIsUnknown_shouldCheckXRealIp() {
        ServerRequest request = mock(ServerRequest.class);
        ServerRequest.Headers headers = mock(ServerRequest.Headers.class);
        
        when(request.headers()).thenReturn(headers);
        when(headers.firstHeader("X-Forwarded-For")).thenReturn("unknown");
        when(headers.firstHeader("X-Real-IP")).thenReturn("192.168.1.200");
        when(request.remoteAddress()).thenReturn(Optional.empty());
        
        String ip = IpUtils.getClientIp(request);
        assertEquals("192.168.1.200", ip);
    }

    @Test
    @DisplayName("当X-Real-IP存在时,应返回该IP")
    void getClientIp_whenXRealIpExists_shouldReturnIt() {
        ServerRequest request = mock(ServerRequest.class);
        ServerRequest.Headers headers = mock(ServerRequest.Headers.class);
        
        when(request.headers()).thenReturn(headers);
        when(headers.firstHeader("X-Forwarded-For")).thenReturn(null);
        when(headers.firstHeader("X-Real-IP")).thenReturn("192.168.1.200");
        when(request.remoteAddress()).thenReturn(Optional.empty());
        
        String ip = IpUtils.getClientIp(request);
        assertEquals("192.168.1.200", ip);
    }

    @Test
    @DisplayName("当没有代理头时,应使用RemoteAddress")
    void getClientIp_whenNoProxyHeaders_shouldUseRemoteAddress() {
        ServerRequest request = mock(ServerRequest.class);
        ServerRequest.Headers headers = mock(ServerRequest.Headers.class);
        InetSocketAddress socketAddress = mock(InetSocketAddress.class);
        java.net.InetAddress inetAddress = mock(java.net.InetAddress.class);
        
        when(request.headers()).thenReturn(headers);
        when(headers.firstHeader("X-Forwarded-For")).thenReturn(null);
        when(headers.firstHeader("X-Real-IP")).thenReturn(null);
        when(request.remoteAddress()).thenReturn(Optional.of(socketAddress));
        when(socketAddress.getAddress()).thenReturn(inetAddress);
        when(inetAddress.getHostAddress()).thenReturn("192.168.1.50");
        
        String ip = IpUtils.getClientIp(request);
        assertEquals("192.168.1.50", ip);
    }

    @Test
    @DisplayName("当RemoteAddress为IPv6本地地址时,应转换为IPv4")
    void getClientIp_whenRemoteAddressIsIpv6Localhost_shouldConvertToIpv4() {
        ServerRequest request = mock(ServerRequest.class);
        ServerRequest.Headers headers = mock(ServerRequest.Headers.class);
        InetSocketAddress socketAddress = mock(InetSocketAddress.class);
        java.net.InetAddress inetAddress = mock(java.net.InetAddress.class);
        
        when(request.headers()).thenReturn(headers);
        when(headers.firstHeader("X-Forwarded-For")).thenReturn(null);
        when(headers.firstHeader("X-Real-IP")).thenReturn(null);
        when(request.remoteAddress()).thenReturn(Optional.of(socketAddress));
        when(socketAddress.getAddress()).thenReturn(inetAddress);
        when(inetAddress.getHostAddress()).thenReturn("0:0:0:0:0:0:0:1");
        
        String ip = IpUtils.getClientIp(request);
        assertEquals("127.0.0.1", ip);
    }

    @Test
    @DisplayName("当所有IP源都不可用时,应返回unknown")
    void getClientIp_whenAllSourcesFail_shouldReturnUnknown() {
        ServerRequest request = mock(ServerRequest.class);
        ServerRequest.Headers headers = mock(ServerRequest.Headers.class);
        
        when(request.headers()).thenReturn(headers);
        when(headers.firstHeader("X-Forwarded-For")).thenReturn(null);
        when(headers.firstHeader("X-Real-IP")).thenReturn(null);
        when(request.remoteAddress()).thenReturn(Optional.empty());
        
        String ip = IpUtils.getClientIp(request);
        assertEquals("unknown", ip);
    }

    @Test
    @DisplayName("当X-Forwarded-For为空字符串时,应跳过")
    void getClientIp_whenXForwardedForIsEmpty_shouldSkip() {
        ServerRequest request = mock(ServerRequest.class);
        ServerRequest.Headers headers = mock(ServerRequest.Headers.class);
        
        when(request.headers()).thenReturn(headers);
        when(headers.firstHeader("X-Forwarded-For")).thenReturn("");
        when(headers.firstHeader("X-Real-IP")).thenReturn("192.168.1.200");
        when(request.remoteAddress()).thenReturn(Optional.empty());
        
        String ip = IpUtils.getClientIp(request);
        assertEquals("192.168.1.200", ip);
    }
}
