package com.easyagents.agent.runtime.mcp;

import com.easyagents.agent.runtime.AgentRuntimeException;
import com.easyagents.agent.runtime.tool.AgentToolSpec;
import com.easyagents.agent.runtime.tool.operate.AgentOperateToolAdapter;
import com.easyagents.agent.runtime.tool.operate.AgentOperateToolSpec;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * MCP 声明校验器。
 */
public final class McpSpecValidator {

    private McpSpecValidator() {
    }

    /**
     * 校验 MCP 连接配置。
     *
     * @param spec MCP 运行时声明
     */
    public static void validateConnection(McpSpec spec) {
        if (spec == null) {
            throw new AgentRuntimeException("MCP spec is required.");
        }
        if (spec.getName() == null || spec.getName().isBlank()) {
            throw new AgentRuntimeException("MCP name is required.");
        }
        if (spec.getTransportType() == null) {
            throw new AgentRuntimeException("MCP transport type is required: " + spec.getName());
        }
        validateToolAliases(spec);
        switch (spec.getTransportType()) {
            case STDIO -> {
                if (spec.getCommand() == null || spec.getCommand().isBlank()) {
                    throw new AgentRuntimeException("MCP stdio command is required: " + spec.getName());
                }
            }
            case SSE, HTTP -> {
                if (spec.getUrl() == null || spec.getUrl().isBlank()) {
                    throw new AgentRuntimeException("MCP url is required: " + spec.getName());
                }
            }
            default -> throw new AgentRuntimeException("Unsupported MCP transport type: " + spec.getTransportType());
        }
    }

    private static void validateToolAliases(McpSpec spec) {
        Map<String, String> aliases = spec.getToolAliases();
        if (aliases == null || aliases.isEmpty()) {
            return;
        }
        Set<String> runtimeNames = new HashSet<>();
        for (Map.Entry<String, String> entry : aliases.entrySet()) {
            String rawName = entry.getKey();
            String runtimeName = entry.getValue();
            if (rawName == null || rawName.isBlank()) {
                throw new AgentRuntimeException("MCP raw tool name is required: " + spec.getName());
            }
            if (runtimeName == null || runtimeName.isBlank()) {
                throw new AgentRuntimeException("MCP runtime tool name is required: " + spec.getName());
            }
            if (!runtimeNames.add(runtimeName)) {
                throw new AgentRuntimeException("MCP runtime tool alias conflicts: " + runtimeName);
            }
        }
    }

    /**
     * 校验 MCP 工具与既有工具名冲突。
     *
     * @param businessToolSpecs 普通工具声明
     * @param mcpToolSpecs MCP 工具声明
     * @param operateToolSpecs 操作工具声明
     */
    public static void validateToolConflicts(List<AgentToolSpec> businessToolSpecs,
                                             List<AgentToolSpec> mcpToolSpecs,
                                             List<AgentOperateToolSpec> operateToolSpecs) {
        Set<String> names = new HashSet<>();
        addToolNames(names, businessToolSpecs, "Agent tool conflicts with existing tool: ");
        addToolNames(names, mcpToolSpecs, "MCP tool conflicts with existing tool: ");
        Set<String> operateToolNames = new AgentOperateToolAdapter().enabledToolNames(operateToolSpecs);
        for (String operateToolName : operateToolNames) {
            if (!names.add(operateToolName)) {
                throw new AgentRuntimeException("Agent operate tool conflicts with existing tool: " + operateToolName);
            }
        }
    }

    private static void addToolNames(Set<String> names, List<AgentToolSpec> toolSpecs, String messagePrefix) {
        if (toolSpecs == null || toolSpecs.isEmpty()) {
            return;
        }
        for (AgentToolSpec toolSpec : toolSpecs) {
            if (toolSpec == null || toolSpec.getName() == null || toolSpec.getName().isBlank()) {
                continue;
            }
            if (!names.add(toolSpec.getName())) {
                throw new AgentRuntimeException(messagePrefix + toolSpec.getName());
            }
        }
    }
}
