/*
 * Copyright 1999-2025 Alibaba Group Holding Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.alibaba.nacos.ai.service;

import com.alibaba.nacos.ai.constant.McpServerValidationConstants;
import com.alibaba.nacos.api.ai.constant.AiConstants;
import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo;
import com.alibaba.nacos.api.ai.model.mcp.McpServerImportValidationResult;
import com.alibaba.nacos.api.ai.model.mcp.McpServerRemoteServiceConfig;
import com.alibaba.nacos.api.ai.model.mcp.McpServerValidationItem;
import com.alibaba.nacos.api.ai.model.mcp.McpTool;
import com.alibaba.nacos.api.ai.model.mcp.McpToolSpecification;
import com.alibaba.nacos.api.ai.model.mcp.registry.Package;
import com.alibaba.nacos.api.ai.model.mcp.registry.ServerVersionDetail;
import com.alibaba.nacos.api.exception.NacosException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.util.ReflectionTestUtils;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class McpServerValidationServiceTest {

    private McpServerValidationService mcpServerValidationService;

    @Mock
    private McpServerOperationService mcpServerOperationService;

    @BeforeEach
    void setUp() {
        mcpServerValidationService = new McpServerValidationService();
        ReflectionTestUtils.setField(mcpServerValidationService, "mcpServerOperationService", mcpServerOperationService);
    }

    @Test
    void validateServersWithNullServers() throws NacosException {
        String namespaceId = "test-namespace";
        McpServerImportValidationResult result = mcpServerValidationService.validateServers(namespaceId, null);

        assertFalse(result.isValid());
        assertEquals(0, result.getTotalCount());
        assertEquals(0, result.getValidCount());
        assertEquals(0, result.getInvalidCount());
        assertEquals(0, result.getDuplicateCount());
        assertNotNull(result.getErrors());
        assertFalse(result.getErrors().isEmpty());
    }

    @Test
    void validateServersWithEmptyServers() throws NacosException {
        String namespaceId = "test-namespace";
        McpServerImportValidationResult result = mcpServerValidationService.validateServers(namespaceId, new ArrayList<>());

        assertTrue(result.isValid());
        assertEquals(0, result.getTotalCount());
        assertEquals(0, result.getValidCount());
        assertEquals(0, result.getInvalidCount());
        assertEquals(0, result.getDuplicateCount());
        assertNotNull(result.getErrors());
        assertTrue(result.getErrors().isEmpty());
    }

    @Test
    void validateServersWithValidServer() throws NacosException {
        String namespaceId = "test-namespace";
        List<McpServerDetailInfo> servers = new ArrayList<>();

        McpServerDetailInfo validServer = createValidServer();
        servers.add(validServer);

        when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class))).thenReturn(null);

        McpServerImportValidationResult result = mcpServerValidationService.validateServers(namespaceId, servers);

        assertTrue(result.isValid());
        assertEquals(1, result.getTotalCount());
        assertEquals(1, result.getValidCount());
        assertEquals(0, result.getInvalidCount());
        assertEquals(0, result.getDuplicateCount());
        assertNotNull(result.getServers());
        assertEquals(1, result.getServers().size());
        assertEquals(McpServerValidationConstants.STATUS_VALID, result.getServers().get(0).getStatus());
    }

    @Test
    void validateServersWithInvalidServer() throws NacosException {
        final String namespaceId = "test-namespace";
        final List<McpServerDetailInfo> servers = new ArrayList<>();

        McpServerDetailInfo invalidServer = new McpServerDetailInfo();
        initializeVersionDetail(invalidServer);
        invalidServer.setName(""); // Empty name should be invalid
        invalidServer.setProtocol("invalid-protocol");
        invalidServer.setDescription("");

        servers.add(invalidServer);

        when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class))).thenReturn(null);

        McpServerImportValidationResult result = mcpServerValidationService.validateServers(namespaceId, servers);

        assertFalse(result.isValid());
        assertEquals(1, result.getTotalCount());
        assertEquals(0, result.getValidCount());
        assertEquals(1, result.getInvalidCount());
        assertEquals(0, result.getDuplicateCount());
        assertNotNull(result.getServers());
        assertEquals(1, result.getServers().size());
        assertEquals(McpServerValidationConstants.STATUS_INVALID, result.getServers().get(0).getStatus());
    }

    @Test
    void validateServersWithDuplicateServerInBatch() throws NacosException {
        final String namespaceId = "test-namespace";
        List<McpServerDetailInfo> servers = new ArrayList<>();

        McpServerDetailInfo server1 = createValidServer();
        server1.setName("duplicate-server");

        McpServerDetailInfo server2 = createValidServer();
        server2.setName("duplicate-server"); // Same name as server1

        servers.add(server1);
        servers.add(server2);

        when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class))).thenReturn(null);

        McpServerImportValidationResult result = mcpServerValidationService.validateServers(namespaceId, servers);

        assertTrue(result.isValid());
        assertEquals(2, result.getTotalCount());
        assertEquals(1, result.getValidCount());
        assertEquals(0, result.getInvalidCount());
        assertEquals(1, result.getDuplicateCount());
        assertNotNull(result.getServers());
        assertEquals(2, result.getServers().size());
        
        McpServerValidationItem item1 = result.getServers().get(0);
        McpServerValidationItem item2 = result.getServers().get(1);
        
        // First one should be valid
        assertEquals(McpServerValidationConstants.STATUS_VALID, item1.getStatus());
        // Second one should be duplicate
        assertEquals(McpServerValidationConstants.STATUS_DUPLICATE, item2.getStatus());
    }

    @Test
    void validateServersWithExistingServer() throws NacosException {
        final String namespaceId = "test-namespace";
        List<McpServerDetailInfo> servers = new ArrayList<>();

        McpServerDetailInfo server = createValidServer();
        server.setName("existing-server");

        servers.add(server);

        // Mock that server already exists
        McpServerDetailInfo existingServer = createValidServer();
        existingServer.setId(UUID.randomUUID().toString());
        when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class))).thenReturn(existingServer);

        McpServerImportValidationResult result = mcpServerValidationService.validateServers(namespaceId, servers);

        assertTrue(result.isValid());
        assertEquals(1, result.getTotalCount());
        assertEquals(0, result.getValidCount());
        assertEquals(0, result.getInvalidCount());
        assertEquals(1, result.getDuplicateCount());
        assertNotNull(result.getServers());
        assertEquals(1, result.getServers().size());
        McpServerValidationItem item = result.getServers().get(0);
        assertEquals(McpServerValidationConstants.STATUS_DUPLICATE, item.getStatus());
        assertTrue(item.isExists());
    }

    @Test
    void validateServersWithExceptionDuringValidation() throws NacosException {
        String namespaceId = "test-namespace";
        List<McpServerDetailInfo> servers = new ArrayList<>();

        McpServerDetailInfo server = createValidServer();
        servers.add(server);

        // Mock exception during validation
        when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class)))
                .thenThrow(new RuntimeException("Test exception"));

        McpServerImportValidationResult result = mcpServerValidationService.validateServers(namespaceId, servers);

        assertFalse(result.isValid());
        assertEquals(1, result.getTotalCount());
        assertEquals(0, result.getValidCount());
        assertEquals(1, result.getInvalidCount());
        assertEquals(0, result.getDuplicateCount());
        assertNotNull(result.getErrors());
        assertTrue(result.getErrors().isEmpty());
    }

    @Test
    void validateSingleServerWithValidServer() throws Exception {
        String namespaceId = "test-namespace";
        McpServerDetailInfo server = createValidServer();
        server.setName("valid-server");

        when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class))).thenReturn(null);

        // 使用反射调用私有方法
        Method method = McpServerValidationService.class.getDeclaredMethod(
                "validateSingleServer", String.class, McpServerDetailInfo.class, Set.class);
        method.setAccessible(true);
        McpServerValidationItem item = (McpServerValidationItem) method.invoke(
                mcpServerValidationService, namespaceId, server, new HashSet<>());

        assertNotNull(item);
        assertEquals("valid-server", item.getServerName());
        assertEquals(McpServerValidationConstants.STATUS_VALID, item.getStatus());
        assertNotNull(item.getErrors());
        assertTrue(item.getErrors().isEmpty());
        assertFalse(item.isExists());
        assertEquals(server, item.getServer());
    }

    @Test
    void validateSingleServerWithMissingName() throws Exception {
        final String namespaceId = "test-namespace";
        McpServerDetailInfo server = new McpServerDetailInfo();
        initializeVersionDetail(server);
        server.setName(""); // Empty name
        server.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP);
        server.setDescription("Test description");

        when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class))).thenReturn(null);

        // 使用反射调用私有方法
        Method method = McpServerValidationService.class.getDeclaredMethod(
                "validateSingleServer", String.class, McpServerDetailInfo.class, Set.class);
        method.setAccessible(true);
        McpServerValidationItem item = (McpServerValidationItem) method.invoke(
                mcpServerValidationService, namespaceId, server, new HashSet<>());

        assertNotNull(item);
        assertEquals("", item.getServerName());
        assertEquals(McpServerValidationConstants.STATUS_INVALID, item.getStatus());
        assertNotNull(item.getErrors());
        assertFalse(item.getErrors().isEmpty());
        assertTrue(item.getErrors().contains("Server name is required"));
    }

    @Test
    void validateSingleServerWithMissingProtocol() throws Exception {
        final String namespaceId = "test-namespace";
        McpServerDetailInfo server = new McpServerDetailInfo();
        initializeVersionDetail(server);
        server.setName("test-server");
        server.setProtocol(""); // Empty protocol
        server.setDescription("Test description");

        when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class))).thenReturn(null);

        // 使用反射调用私有方法
        Method method = McpServerValidationService.class.getDeclaredMethod(
                "validateSingleServer", String.class, McpServerDetailInfo.class, Set.class);
        method.setAccessible(true);
        McpServerValidationItem item = (McpServerValidationItem) method.invoke(
                mcpServerValidationService, namespaceId, server, new HashSet<>());

        assertNotNull(item);
        assertEquals("test-server", item.getServerName());
        assertEquals(McpServerValidationConstants.STATUS_INVALID, item.getStatus());
        assertNotNull(item.getErrors());
        assertFalse(item.getErrors().isEmpty());
        assertTrue(item.getErrors().contains("Protocol is required"));
    }

    @Test
    void validateSingleServerWithInvalidProtocol() throws Exception {
        final String namespaceId = "test-namespace";
        McpServerDetailInfo server = new McpServerDetailInfo();
        initializeVersionDetail(server);
        server.setName("test-server");
        server.setProtocol("invalid-protocol");
        server.setDescription("Test description");

        when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class))).thenReturn(null);

        // 使用反射调用私有方法
        Method method = McpServerValidationService.class.getDeclaredMethod(
                "validateSingleServer", String.class, McpServerDetailInfo.class, Set.class);
        method.setAccessible(true);
        McpServerValidationItem item = (McpServerValidationItem) method.invoke(
                mcpServerValidationService, namespaceId, server, new HashSet<>());

        assertNotNull(item);
        assertEquals("test-server", item.getServerName());
        assertEquals(McpServerValidationConstants.STATUS_INVALID, item.getStatus());
        assertNotNull(item.getErrors());
        assertFalse(item.getErrors().isEmpty());
        assertTrue(item.getErrors().contains("Invalid protocol: invalid-protocol"));
    }

    @Test
    void validateSingleServerWithMissingDescription() throws Exception {
        final String namespaceId = "test-namespace";
        McpServerDetailInfo server = new McpServerDetailInfo();
        initializeVersionDetail(server);
        server.setName("test-server");
        server.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP);
        server.setDescription(""); // Empty description

        when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class))).thenReturn(null);

        // 使用反射调用私有方法
        Method method = McpServerValidationService.class.getDeclaredMethod(
                "validateSingleServer", String.class, McpServerDetailInfo.class, Set.class);
        method.setAccessible(true);
        McpServerValidationItem item = (McpServerValidationItem) method.invoke(
                mcpServerValidationService, namespaceId, server, new HashSet<>());

        assertNotNull(item);
        assertEquals("test-server", item.getServerName());
        assertEquals(McpServerValidationConstants.STATUS_INVALID, item.getStatus());
        assertNotNull(item.getErrors());
        assertFalse(item.getErrors().isEmpty());
        assertTrue(item.getErrors().contains("Description is required"));
    }

    @Test
    void validateSingleServerWithDuplicateInBatch() throws Exception {
        String namespaceId = "test-namespace";
        McpServerDetailInfo server = createValidServer();
        server.setName("duplicate-server");

        when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class))).thenReturn(null);

        // Add server name + version to existing names set to simulate duplicate in batch
        java.util.Set<String> existingNames = new java.util.HashSet<>();
        existingNames.add("duplicate-server" + server.getVersionDetail().getVersion());

        // 使用反射调用私有方法
        Method method = McpServerValidationService.class.getDeclaredMethod(
                "validateSingleServer", String.class, McpServerDetailInfo.class, Set.class);
        method.setAccessible(true);
        McpServerValidationItem item = (McpServerValidationItem) method.invoke(
                mcpServerValidationService, namespaceId, server, existingNames);

        assertNotNull(item);
        assertEquals("duplicate-server", item.getServerName());
        assertEquals(McpServerValidationConstants.STATUS_DUPLICATE, item.getStatus());
        assertNotNull(item.getErrors());
        assertFalse(item.getErrors().isEmpty());
        assertTrue(item.getErrors().contains("Duplicate server name in import batch: duplicate-server"));
    }

    @Test
    void validateSingleServerWithExceptionDuringExistenceCheck() throws Exception {
        String namespaceId = "test-namespace";
        McpServerDetailInfo server = createValidServer();
        server.setName("test-server");

        when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class)))
                .thenThrow(new RuntimeException("Test exception"));

        // 使用反射调用私有方法
        Method method = McpServerValidationService.class.getDeclaredMethod(
                "validateSingleServer", String.class, McpServerDetailInfo.class, Set.class);
        method.setAccessible(true);
        McpServerValidationItem item = (McpServerValidationItem) method.invoke(
                mcpServerValidationService, namespaceId, server, new HashSet<>());

        assertNotNull(item);
        assertEquals("test-server", item.getServerName());
        // Should still be invalid if existence check fails
        assertEquals(McpServerValidationConstants.STATUS_INVALID, item.getStatus());
        assertNotNull(item.getErrors());
        assertFalse(item.getErrors().isEmpty());
        assertTrue(item.getErrors().contains("Error checking existing server: Test exception"));
    }

    @Test
    void isValidProtocolWithValidProtocols() throws Exception {
        // 使用反射调用私有方法
        Method method = McpServerValidationService.class.getDeclaredMethod("isValidProtocol", String.class);
        method.setAccessible(true);

        // Test all valid protocols
        assertTrue((Boolean) method.invoke(mcpServerValidationService, AiConstants.Mcp.MCP_PROTOCOL_STDIO));
        assertTrue((Boolean) method.invoke(mcpServerValidationService, AiConstants.Mcp.MCP_PROTOCOL_SSE));
        assertTrue((Boolean) method.invoke(mcpServerValidationService, AiConstants.Mcp.MCP_PROTOCOL_STREAMABLE));
        assertTrue((Boolean) method.invoke(mcpServerValidationService, AiConstants.Mcp.MCP_PROTOCOL_HTTP));
        assertTrue((Boolean) method.invoke(mcpServerValidationService, AiConstants.Mcp.MCP_PROTOCOL_DUBBO));
    }

    @Test
    void isValidProtocolWithInvalidProtocol() throws Exception {
        // 使用反射调用私有方法
        Method method = McpServerValidationService.class.getDeclaredMethod("isValidProtocol", String.class);
        method.setAccessible(true);

        assertFalse((Boolean) method.invoke(mcpServerValidationService, "invalid-protocol"));
        assertFalse((Boolean) method.invoke(mcpServerValidationService, ""));
        assertFalse((Boolean) method.invoke(mcpServerValidationService, (String) null));
    }

    @Test
    void validateProtocolSpecificConfigWithValidStdioConfig() throws Exception {
        McpServerDetailInfo server = new McpServerDetailInfo();
        initializeVersionDetail(server);
        server.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_STDIO);
        
        // Valid stdio config with localServerConfig
        Map<String, Object> localConfig = new HashMap<>();
        localConfig.put("command", "test-command");
        server.setLocalServerConfig(localConfig);

        List<String> errors = new ArrayList<>();
        
        // 使用反射调用私有方法
        Method method = McpServerValidationService.class.getDeclaredMethod(
                "validateProtocolSpecificConfig", McpServerDetailInfo.class, List.class);
        method.setAccessible(true);
        method.invoke(mcpServerValidationService, server, errors);

        assertNotNull(errors);
        assertTrue(errors.isEmpty());
    }

    @Test
    void validateProtocolSpecificConfigWithValidStdioConfigWithPackages() throws Exception {
        McpServerDetailInfo server = new McpServerDetailInfo();
        initializeVersionDetail(server);
        server.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_STDIO);
        
        // Valid stdio config with packages
        List<Package> packages = new ArrayList<>();
        packages.add(new Package());
        server.setPackages(packages);
        server.setLocalServerConfig(null); // No local config, but has packages

        List<String> errors = new ArrayList<>();
        
        // 使用反射调用私有方法
        Method method = McpServerValidationService.class.getDeclaredMethod(
                "validateProtocolSpecificConfig", McpServerDetailInfo.class, List.class);
        method.setAccessible(true);
        method.invoke(mcpServerValidationService, server, errors);

        assertNotNull(errors);
        assertTrue(errors.isEmpty());
    }

    @Test
    void validateProtocolSpecificConfigWithInvalidStdioConfig() throws Exception {
        McpServerDetailInfo server = new McpServerDetailInfo();
        initializeVersionDetail(server);
        server.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_STDIO);
        
        // Invalid stdio config - no local config and no packages
        server.setLocalServerConfig(null);
        server.setPackages(null);

        List<String> errors = new ArrayList<>();
        
        // 使用反射调用私有方法
        Method method = McpServerValidationService.class.getDeclaredMethod(
                "validateProtocolSpecificConfig", McpServerDetailInfo.class, List.class);
        method.setAccessible(true);
        method.invoke(mcpServerValidationService, server, errors);

        assertNotNull(errors);
        assertFalse(errors.isEmpty());
        assertTrue(errors.contains("Local server configuration or packages are required for stdio protocol"));
    }

    @Test
    void validateProtocolSpecificConfigWithValidRemoteConfig() throws Exception {
        McpServerDetailInfo server = new McpServerDetailInfo();
        initializeVersionDetail(server);
        server.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP);
        
        // Valid remote config
        McpServerRemoteServiceConfig remoteConfig = new McpServerRemoteServiceConfig();
        server.setRemoteServerConfig(remoteConfig);

        List<String> errors = new ArrayList<>();
        
        // 使用反射调用私有方法
        Method method = McpServerValidationService.class.getDeclaredMethod(
                "validateProtocolSpecificConfig", McpServerDetailInfo.class, List.class);
        method.setAccessible(true);
        method.invoke(mcpServerValidationService, server, errors);

        assertNotNull(errors);
        assertTrue(errors.isEmpty());
    }

    @Test
    void validateProtocolSpecificConfigWithInvalidRemoteConfig() throws Exception {
        McpServerDetailInfo server = new McpServerDetailInfo();
        initializeVersionDetail(server);
        server.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP);
        
        // Invalid remote config - no remote config
        server.setRemoteServerConfig(null);

        List<String> errors = new ArrayList<>();
        
        // 使用反射调用私有方法
        Method method = McpServerValidationService.class.getDeclaredMethod(
                "validateProtocolSpecificConfig", McpServerDetailInfo.class, List.class);
        method.setAccessible(true);
        method.invoke(mcpServerValidationService, server, errors);

        assertNotNull(errors);
        assertFalse(errors.isEmpty());
        assertTrue(errors.contains("Remote server configuration is required for " + AiConstants.Mcp.MCP_PROTOCOL_HTTP + " protocol"));
    }

    @Test
    void validateProtocolSpecificConfigWithValidToolSpec() throws Exception {
        McpServerDetailInfo server = new McpServerDetailInfo();
        initializeVersionDetail(server);
        server.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP);
        server.setRemoteServerConfig(new McpServerRemoteServiceConfig());
        
        // Valid tool spec with tools
        McpToolSpecification toolSpec = new McpToolSpecification();
        List<McpTool> tools = new ArrayList<>();
        McpTool tool = new McpTool();
        tool.setName("test-tool");
        tools.add(tool);
        toolSpec.setTools(tools);
        server.setToolSpec(toolSpec);

        List<String> errors = new ArrayList<>();
        
        // 使用反射调用私有方法
        Method method = McpServerValidationService.class.getDeclaredMethod(
                "validateProtocolSpecificConfig", McpServerDetailInfo.class, List.class);
        method.setAccessible(true);
        method.invoke(mcpServerValidationService, server, errors);

        assertNotNull(errors);
        assertTrue(errors.isEmpty());
    }

    @Test
    void validateProtocolSpecificConfigWithInvalidToolSpec() throws Exception {
        McpServerDetailInfo server = new McpServerDetailInfo();
        initializeVersionDetail(server);
        server.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP);
        server.setRemoteServerConfig(new McpServerRemoteServiceConfig());
        
        // Invalid tool spec - empty tools list
        McpToolSpecification toolSpec = new McpToolSpecification();
        toolSpec.setTools(new ArrayList<>()); // Empty tools list
        server.setToolSpec(toolSpec);

        List<String> errors = new ArrayList<>();
        
        // 使用反射调用私有方法
        Method method = McpServerValidationService.class.getDeclaredMethod(
                "validateProtocolSpecificConfig", McpServerDetailInfo.class, List.class);
        method.setAccessible(true);
        method.invoke(mcpServerValidationService, server, errors);

        assertNotNull(errors);
        assertFalse(errors.isEmpty());
        assertTrue(errors.contains("Tool specification should contain at least one tool"));
    }

    @Test
    void validateProtocolSpecificConfigWithNullToolSpec() throws Exception {
        McpServerDetailInfo server = new McpServerDetailInfo();
        initializeVersionDetail(server);
        server.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP);
        server.setRemoteServerConfig(new McpServerRemoteServiceConfig());
        
        // No tool spec - should be valid
        server.setToolSpec(null);

        List<String> errors = new ArrayList<>();
        
        // 使用反射调用私有方法
        Method method = McpServerValidationService.class.getDeclaredMethod(
                "validateProtocolSpecificConfig", McpServerDetailInfo.class, List.class);
        method.setAccessible(true);
        method.invoke(mcpServerValidationService, server, errors);

        assertNotNull(errors);
        assertTrue(errors.isEmpty());
    }

    @Test
    void validateSingleServerDoesNotCallIndexWhenNameIsBlank() throws Exception {
        final String namespaceId = "test-namespace";
        McpServerDetailInfo server = new McpServerDetailInfo();
        initializeVersionDetail(server);
        server.setName(""); // Blank name
        server.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP);
        server.setDescription("Test description");

        when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class))).thenReturn(null);

        // Should not call the index when name is blank
        // 使用反射调用私有方法
        Method method = McpServerValidationService.class.getDeclaredMethod(
                "validateSingleServer", String.class, McpServerDetailInfo.class, Set.class);
        method.setAccessible(true);
        McpServerValidationItem item = (McpServerValidationItem) method.invoke(
                mcpServerValidationService, namespaceId, server, new HashSet<>());

        assertNotNull(item);
        assertEquals("", item.getServerName());
        assertEquals(McpServerValidationConstants.STATUS_INVALID, item.getStatus());
        assertEquals("Server name is required", item.getErrors().get(0));
    }

    @Test
    void validateServersWithMixedValidAndInvalidServers() throws NacosException {
        final String namespaceId = "test-namespace";
        List<McpServerDetailInfo> servers = new ArrayList<>();

        // Add a valid server
        McpServerDetailInfo validServer = createValidServer();
        validServer.setName("valid-server");
        servers.add(validServer);

        // Add an invalid server
        McpServerDetailInfo invalidServer = new McpServerDetailInfo();
        initializeVersionDetail(invalidServer);
        invalidServer.setName(""); // Missing name
        invalidServer.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP);
        invalidServer.setDescription("Test description");
        servers.add(invalidServer);

        // Add a duplicate server
        McpServerDetailInfo duplicateServer = createValidServer();
        duplicateServer.setName("valid-server"); // Same name as validServer
        servers.add(duplicateServer);

        when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class))).thenReturn(null);

        McpServerImportValidationResult result = mcpServerValidationService.validateServers(namespaceId, servers);

        assertFalse(result.isValid());
        assertEquals(3, result.getTotalCount());
        assertEquals(1, result.getValidCount());
        assertEquals(1, result.getInvalidCount());
        assertEquals(1, result.getDuplicateCount());
        assertNotNull(result.getServers());
        assertEquals(3, result.getServers().size());
    }

    private void initializeVersionDetail(McpServerDetailInfo server) {
        if (server.getVersionDetail() == null) {
            ServerVersionDetail versionDetail = new ServerVersionDetail();
            versionDetail.setVersion("1.0.0");
            server.setVersionDetail(versionDetail);
        }
    }

    private McpServerDetailInfo createValidServer() {
        McpServerDetailInfo server = new McpServerDetailInfo();
        initializeVersionDetail(server);
        server.setName("test-server");
        server.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP);
        server.setDescription("Test description");
        
        McpServerRemoteServiceConfig remoteConfig = new McpServerRemoteServiceConfig();
        server.setRemoteServerConfig(remoteConfig);
        
        return server;
    }
}