package tech.easyflow.agent.runtime.tool;

import com.easyagents.agent.runtime.hitl.AgentToolApprovalRequest;
import com.easyagents.agent.runtime.mcp.McpSpec;
import com.easyagents.agent.runtime.mcp.McpTransportType;
import com.easyagents.agent.runtime.tool.*;
import com.easyagents.agent.runtime.tool.asynctool.AsyncToolSpec;
import com.easyagents.agent.runtime.tool.asynctool.AsyncToolSpecExpander;
import com.easyagents.core.model.chat.tool.Parameter;
import com.easyagents.core.model.chat.tool.Tool;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import tech.easyflow.agent.entity.Agent;
import tech.easyflow.agent.entity.AgentToolBinding;
import tech.easyflow.agent.enums.AgentToolType;
import tech.easyflow.agent.runtime.asynctool.AgentAsyncToolTaskStore;
import tech.easyflow.agent.runtime.asynctool.PluginAsyncSubTools;
import tech.easyflow.agent.runtime.asynctool.WorkflowAsyncSubTools;
import tech.easyflow.ai.easyagents.tool.ChatToolNameHelper;
import tech.easyflow.ai.entity.Mcp;
import tech.easyflow.ai.entity.PluginItem;
import tech.easyflow.ai.entity.Workflow;
import tech.easyflow.ai.service.McpService;
import tech.easyflow.ai.service.PluginItemService;
import tech.easyflow.ai.service.WorkflowService;
import tech.easyflow.common.web.exceptions.BusinessException;

import javax.annotation.Resource;
import java.math.BigInteger;
import java.time.Duration;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Agent 工具运行时编译器。
 */
@Component
public class AgentToolRuntimeCompiler {

    private static final Pattern MCP_INPUT_PATTERN = Pattern.compile("\\$\\{input:([A-Za-z0-9_.-]+)}");
    private static final Pattern ASYNC_SAFE_NAME = Pattern.compile("^[a-z][a-z0-9_]*$");

    @Resource
    private WorkflowService workflowService;
    @Resource
    private PluginItemService pluginItemService;
    @Resource
    private McpService mcpService;
    @Resource
    private ObjectMapper objectMapper;
    @Resource
    private WorkflowToolExecutor workflowToolExecutor;
    @Resource
    private PluginToolExecutor pluginToolExecutor;
    @Resource
    private AgentAsyncToolTaskStore asyncToolTaskStore;
    @Resource(name = "agentAsyncToolExecutor")
    private ThreadPoolTaskExecutor agentAsyncToolExecutor;

    /**
     * 编译 Agent 工具配置。
     *
     * @param agent Agent 业务定义
     * @return 工具编译结果
     */
    public AgentToolRuntimeCompilation compile(Agent agent) {
        AgentToolRuntimeCompilation compilation = new AgentToolRuntimeCompilation();
        if (agent == null || agent.getToolBindings() == null) {
            return compilation;
        }
        List<AgentToolSpec> specs = new ArrayList<>();
        Map<String, AgentToolInvoker> invokers = new LinkedHashMap<>();
        List<McpSpec> mcpSpecs = new ArrayList<>();
        Map<BigInteger, McpSpec> mcpSpecMap = new LinkedHashMap<>();
        Set<String> compiledToolNames = new LinkedHashSet<>();
        AsyncToolSpecExpander asyncExpander = new AsyncToolSpecExpander();
        for (AgentToolBinding binding : agent.getToolBindings()) {
            if (!Boolean.TRUE.equals(binding.getEnabled())) {
                continue;
            }
            AgentToolType type = AgentToolType.from(binding.getToolType());
            if (type == AgentToolType.MCP) {
                McpSpec mcpSpec = mcpSpecMap.computeIfAbsent(binding.getTargetId(), ignored -> buildMcpSpec(binding));
                applyMcpToolBinding(mcpSpec, binding);
                if (!mcpSpecs.contains(mcpSpec)) {
                    mcpSpecs.add(mcpSpec);
                }
                continue;
            }
            if (executionMode(binding) == AgentToolExecutionMode.ASYNC) {
                AsyncToolSpec asyncSpec = buildAsyncToolSpec(type, binding);
                addExpandedTools(specs, invokers, compiledToolNames,
                        asyncExpander.expandSpecs(asyncSpec),
                        asyncExpander.expandInvokers(asyncSpec));
                continue;
            }
            CompiledSyncTool syncTool = buildSyncTool(type, binding);
            addCompiledTool(specs, invokers, compiledToolNames, syncTool.spec(), syncTool.invoker());
        }
        compilation.setToolSpecs(specs);
        compilation.setMcpSpecs(mcpSpecs);
        compilation.setToolInvokers(invokers);
        return compilation;
    }

    private void addExpandedTools(List<AgentToolSpec> specs,
                                  Map<String, AgentToolInvoker> invokers,
                                  Set<String> compiledToolNames,
                                  List<AgentToolSpec> expandedSpecs,
                                  Map<String, AgentToolInvoker> expandedInvokers) {
        for (AgentToolSpec spec : expandedSpecs) {
            addCompiledTool(specs, invokers, compiledToolNames, spec,
                    expandedInvokers == null ? null : expandedInvokers.get(spec.getName()));
        }
    }

    private void addCompiledTool(List<AgentToolSpec> specs,
                                 Map<String, AgentToolInvoker> invokers,
                                 Set<String> compiledToolNames,
                                 AgentToolSpec spec,
                                 AgentToolInvoker invoker) {
        String name = spec == null ? null : spec.getName();
        if (name == null || name.isBlank()) {
            throw new BusinessException("Agent 工具运行名不能为空");
        }
        if (!compiledToolNames.add(name)) {
            throw new BusinessException("Agent 工具运行名冲突：" + name + "，请调整工具名称");
        }
        specs.add(spec);
        if (invoker != null) {
            invokers.put(name, invoker);
        }
    }

    private CompiledSyncTool buildSyncTool(AgentToolType type, AgentToolBinding binding) {
        if (type == AgentToolType.WORKFLOW) {
            Workflow workflow = requireWorkflow(binding);
            Tool tool = workflowToolExecutor.buildTool(workflow);
            AgentToolSpec spec = toToolSpec(tool, binding);
            AgentToolInvoker invoker = (arguments, context) -> invokeSafely(spec.getName(),
                    () -> workflowToolExecutor.execute(workflow, arguments).getResult());
            return new CompiledSyncTool(spec, invoker);
        }
        if (type == AgentToolType.PLUGIN) {
            PluginItem pluginItem = requirePlugin(binding);
            Tool tool = pluginToolExecutor.buildTool(pluginItem);
            AgentToolSpec spec = toToolSpec(tool, binding);
            AgentToolInvoker invoker = (arguments, context) -> invokeSafely(spec.getName(),
                    () -> pluginToolExecutor.execute(pluginItem, arguments).getResult());
            return new CompiledSyncTool(spec, invoker);
        }
        throw new BusinessException("不支持的 Agent 工具类型：" + type.name());
    }

    private AsyncToolSpec buildAsyncToolSpec(AgentToolType type, AgentToolBinding binding) {
        if (type == AgentToolType.WORKFLOW) {
            Workflow workflow = requireWorkflow(binding);
            Tool tool = workflowToolExecutor.buildTool(workflow);
            String asyncName = asyncToolName(tool, binding, "workflow");
            String toolDisplayName = displayName(tool, workflow.getTitle());
            AsyncToolSpec spec = baseAsyncSpec(asyncName, tool, binding, toolDisplayName);
            spec.setSubTools(new WorkflowAsyncSubTools(workflow, asyncName, toolDisplayName,
                    workflowToolExecutor, asyncToolTaskStore, agentAsyncToolExecutor));
            return spec;
        }
        if (type == AgentToolType.PLUGIN) {
            PluginItem pluginItem = requirePlugin(binding);
            Tool tool = pluginToolExecutor.buildTool(pluginItem);
            String asyncName = asyncToolName(tool, binding, "plugin");
            String toolDisplayName = displayName(tool, pluginItem.getName());
            AsyncToolSpec spec = baseAsyncSpec(asyncName, tool, binding, toolDisplayName);
            spec.setSubTools(new PluginAsyncSubTools(pluginItem, asyncName, toolDisplayName,
                    pluginToolExecutor, asyncToolTaskStore, agentAsyncToolExecutor));
            return spec;
        }
        throw new BusinessException("不支持的 Agent 异步工具类型：" + type.name());
    }

    private AsyncToolSpec baseAsyncSpec(String asyncName, Tool tool, AgentToolBinding binding, String toolDisplayName) {
        AsyncToolSpec spec = new AsyncToolSpec();
        spec.setName(asyncName);
        spec.setDescription(safeDescription(tool == null ? null : tool.getDescription()));
        spec.setSubmitParametersSchema(toSchema(tool == null ? null : tool.getParameters()));
        spec.setApprovalRequired(Boolean.TRUE.equals(binding.getHitlEnabled()));
        if (Boolean.TRUE.equals(binding.getHitlEnabled())) {
            spec.setApprovalRequest(buildBindingApprovalRequest(binding));
        }
        spec.getMetadata().put("bindingId", binding.getId());
        spec.getMetadata().put("targetId", binding.getTargetId());
        spec.getMetadata().put("toolType", binding.getToolType());
        // 异步子工具名服务 runtime 调用，事件和聊天展示必须保留业务名称。
        spec.getMetadata().put("toolDisplayName", toolDisplayName);
        return spec;
    }

    private AgentToolResult invokeSafely(String toolName, ToolCall call) {
        try {
            Object result = call.invoke();
            return AgentToolResult.success(result == null ? "" : String.valueOf(result));
        } catch (Exception e) {
            return AgentToolResult.failure(e.getMessage() == null ? "工具执行失败" : e.getMessage());
        }
    }

    private AgentToolExecutionMode executionMode(AgentToolBinding binding) {
        Object value = binding == null || binding.getOptionsJson() == null ? null : binding.getOptionsJson().get("executionMode");
        return AgentToolExecutionMode.from(value == null ? null : String.valueOf(value));
    }

    private Workflow requireWorkflow(AgentToolBinding binding) {
        Workflow workflow = snapshotOrPublishedWorkflow(binding);
        if (workflow == null) {
            throw new BusinessException("绑定工作流不存在");
        }
        return workflow;
    }

    private PluginItem requirePlugin(AgentToolBinding binding) {
        PluginItem pluginItem = snapshotOrCurrentPlugin(binding);
        if (pluginItem == null) {
            throw new BusinessException("绑定插件不存在");
        }
        return pluginItem;
    }

    private AgentToolSpec toToolSpec(Tool tool, AgentToolBinding binding) {
        AgentToolSpec spec = new AgentToolSpec();
        String name = resolveRuntimeToolName(tool, binding);
        spec.setName(name);
        spec.setDescription(safeDescription(tool == null ? null : tool.getDescription()));
        spec.setCategory(AgentToolCategory.valueOf(AgentToolType.from(binding.getToolType()).name()));
        spec.setParametersSchema(toSchema(tool == null ? null : tool.getParameters()));
        spec.setApprovalRequired(Boolean.TRUE.equals(binding.getHitlEnabled()));
        if (Boolean.TRUE.equals(binding.getHitlEnabled())) {
            spec.setApprovalRequest(buildBindingApprovalRequest(binding));
        }
        spec.getMetadata().put("bindingId", binding.getId());
        spec.getMetadata().put("targetId", binding.getTargetId());
        spec.getMetadata().put("toolType", binding.getToolType());
        spec.getMetadata().put("toolDisplayName", displayName(tool, binding.getToolName()));
        return spec;
    }

    private AgentToolApprovalRequest buildBindingApprovalRequest(AgentToolBinding binding) {
        AgentToolApprovalRequest request = new AgentToolApprovalRequest();
        String name = binding == null ? "工具" : binding.getToolName();
        request.setApprovalPrompt(stringValue(binding == null ? null : binding.getHitlConfigJson(), "prompt", "是否批准执行工具：" + name));
        Map<String, Object> metadata = sanitizedHitlMetadata(binding == null ? null : binding.getHitlConfigJson());
        if (binding != null) {
            metadata.put("toolType", binding.getToolType());
            metadata.put("bindingId", binding.getId());
            metadata.put("targetId", binding.getTargetId());
        }
        request.setMetadata(metadata);
        return request;
    }

    private McpSpec buildMcpSpec(AgentToolBinding binding) {
        Mcp mcp = snapshotOrCurrentMcp(binding);
        if (mcp == null) {
            throw new BusinessException("绑定 MCP 不存在");
        }
        Map.Entry<String, Map<String, Object>> server = firstMcpServer(mcp);
        Map<String, Object> serverConfig = server.getValue();
        McpSpec spec = new McpSpec();
        spec.setName(mcpRuntimeName(mcp));
        spec.setDescription(firstNonBlank(mcp.getDescription(), mcp.getTitle()));
        spec.setTransportType(parseMcpTransportType(mcp, serverConfig));
        spec.setCommand(resolveMcpInput(stringValue(serverConfig, "command", null)));
        spec.setArgs(resolveMcpInputs(stringListValue(serverConfig, "args")));
        spec.setEnv(resolveMcpInputMap(stringMapValue(serverConfig, "env")));
        spec.setUrl(resolveMcpInput(stringValue(serverConfig, "url", null)));
        spec.setHeaders(resolveMcpInputMap(stringMapValue(serverConfig, "headers")));
        spec.setQueryParams(resolveMcpInputMap(stringMapValue(serverConfig, "queryParams")));
        Duration timeout = durationValue(serverConfig, "timeout");
        if (timeout != null) {
            spec.setTimeout(timeout);
        }
        Duration initializationTimeout = durationValue(serverConfig, "initializationTimeout");
        if (initializationTimeout != null) {
            spec.setInitializationTimeout(initializationTimeout);
        }
        spec.setGroupName(mcpRuntimeName(mcp));
        spec.setApprovalRequired(Boolean.TRUE.equals(mcp.getApprovalRequired()));
        spec.setApprovalRequest(buildMcpApprovalRequest(mcp));
        spec.setToolNamePrefix(mcpRuntimeToolPrefix(mcp.getId()));
        spec.getMetadata().put("toolType", AgentToolType.MCP.name());
        spec.getMetadata().put("mcpId", String.valueOf(mcp.getId()));
        spec.getMetadata().put("mcpTitle", mcp.getTitle());
        spec.getMetadata().put("serverName", server.getKey());
        return spec;
    }

    private void applyMcpToolBinding(McpSpec spec, AgentToolBinding binding) {
        if (Boolean.TRUE.equals(binding.getHitlEnabled())) {
            spec.setApprovalRequired(true);
            spec.setApprovalRequest(buildBindingApprovalRequest(binding));
        }
    }

    private AgentToolApprovalRequest buildMcpApprovalRequest(Mcp mcp) {
        AgentToolApprovalRequest request = new AgentToolApprovalRequest();
        request.setApprovalPrompt("是否批准执行 MCP 工具：" + firstNonBlank(mcp.getTitle(), mcpRuntimeName(mcp)));
        Map<String, Object> metadata = new LinkedHashMap<>();
        metadata.put("toolType", AgentToolType.MCP.name());
        metadata.put("mcpId", String.valueOf(mcp.getId()));
        metadata.put("mcpTitle", mcp.getTitle());
        request.setMetadata(metadata);
        return request;
    }

    private Workflow snapshotOrPublishedWorkflow(AgentToolBinding binding) {
        if (binding.getResourceSnapshot() != null && !binding.getResourceSnapshot().isEmpty()) {
            Workflow workflow = objectMapper.convertValue(binding.getResourceSnapshot(), Workflow.class);
            workflow.setId(firstNonNull(workflow.getId(), binding.getTargetId()));
            return workflow;
        }
        return workflowService.getPublishedById(binding.getTargetId());
    }

    private PluginItem snapshotOrCurrentPlugin(AgentToolBinding binding) {
        if (binding.getResourceSnapshot() != null && !binding.getResourceSnapshot().isEmpty()) {
            PluginItem pluginItem = objectMapper.convertValue(binding.getResourceSnapshot(), PluginItem.class);
            pluginItem.setId(firstNonNull(pluginItem.getId(), binding.getTargetId()));
            return pluginItem;
        }
        return pluginItemService.getById(binding.getTargetId());
    }

    private Mcp snapshotOrCurrentMcp(AgentToolBinding binding) {
        if (binding.getResourceSnapshot() != null && !binding.getResourceSnapshot().isEmpty()) {
            Mcp mcp = objectMapper.convertValue(binding.getResourceSnapshot(), Mcp.class);
            mcp.setId(firstNonNull(mcp.getId(), binding.getTargetId()));
            return mcp;
        }
        return mcpService.getById(binding.getTargetId());
    }

    private Map<String, Object> toSchema(Parameter[] parameters) {
        Map<String, Object> schema = new LinkedHashMap<>();
        Map<String, Object> properties = new LinkedHashMap<>();
        List<String> required = new ArrayList<>();
        if (parameters != null) {
            for (Parameter parameter : parameters) {
                properties.put(parameter.getName(), parameterSchema(parameter));
                if (parameter.isRequired()) {
                    required.add(parameter.getName());
                }
            }
        }
        schema.put("type", "object");
        schema.put("properties", properties);
        schema.put("required", required);
        return schema;
    }

    private Map<String, Object> parameterSchema(Parameter parameter) {
        Map<String, Object> schema = new LinkedHashMap<>();
        schema.put("type", parameter.getType() == null ? "string" : parameter.getType());
        putOptionalString(schema, "description", parameter.getDescription());
        if (parameter.getChildren() != null && !parameter.getChildren().isEmpty()) {
            Map<String, Object> children = new LinkedHashMap<>();
            for (Parameter child : parameter.getChildren()) {
                if (child != null && child.getName() != null && !child.getName().isBlank()) {
                    children.put(child.getName(), parameterSchema(child));
                }
            }
            if ("array".equalsIgnoreCase(parameter.getType())) {
                schema.put("items", firstArrayItemSchema(parameter.getChildren()));
            } else {
                schema.put("properties", children);
            }
        }
        return schema;
    }

    private Map<String, Object> firstArrayItemSchema(List<Parameter> children) {
        return children.stream().filter(Objects::nonNull).findFirst()
                .map(this::parameterSchema)
                .orElse(Map.of("type", "string"));
    }

    private void putOptionalString(Map<String, Object> target, String key, String value) {
        if (value != null && !value.isBlank()) {
            target.put(key, value);
        }
    }

    private String resolveRuntimeToolName(Tool tool, AgentToolBinding binding) {
        String bindingName = binding == null ? null : binding.getToolName();
        if (ChatToolNameHelper.isSafeToolName(bindingName)) {
            return bindingName;
        }
        String toolName = tool == null ? null : tool.getName();
        if (ChatToolNameHelper.isSafeToolName(toolName)) {
            return toolName;
        }
        BigInteger targetId = binding == null ? null : binding.getTargetId();
        return ChatToolNameHelper.buildFallbackName("tool", targetId);
    }

    private String asyncToolName(Tool tool, AgentToolBinding binding, String fallbackPrefix) {
        String base = resolveRuntimeToolName(tool, binding).toLowerCase(Locale.ROOT)
                .replaceAll("[^a-z0-9_]", "_")
                .replaceAll("_+", "_");
        if (!base.isBlank() && Character.isDigit(base.charAt(0))) {
            base = fallbackPrefix + "_" + base;
        }
        if (ASYNC_SAFE_NAME.matcher(base).matches()) {
            return base;
        }
        return fallbackPrefix + "_" + (binding == null || binding.getTargetId() == null ? "unknown" : binding.getTargetId());
    }

    private String displayName(Tool tool, String fallback) {
        String value = tool == null ? null : tool.getName();
        return firstNonBlank(firstNonBlank(fallback, value), "工具调用");
    }

    private String safeDescription(String description) {
        return description == null || description.isBlank() ? "EasyFlow Agent 工具" : description;
    }

    private Map<String, Object> sanitizedHitlMetadata(Map<String, Object> config) {
        Map<String, Object> metadata = new LinkedHashMap<>();
        if (config != null) {
            config.forEach((key, value) -> {
                if (!isHitlPromptKey(key)) {
                    metadata.put(key, value);
                }
            });
        }
        return metadata;
    }

    private boolean isHitlPromptKey(String key) {
        if (key == null) {
            return false;
        }
        String normalized = key.trim();
        return "prompt".equalsIgnoreCase(normalized)
                || "question".equalsIgnoreCase(normalized)
                || "approvalPrompt".equalsIgnoreCase(normalized);
    }

    private Map.Entry<String, Map<String, Object>> firstMcpServer(Mcp mcp) {
        Map<String, Object> config = parseMcpConfig(mcp);
        Map<String, Object> servers = mapValue(config, "mcpServers");
        if (servers.isEmpty()) {
            throw new BusinessException("MCP 配置 JSON 中没有找到任何 MCP 服务名称");
        }
        Map.Entry<String, Object> first = servers.entrySet().iterator().next();
        if (!(first.getValue() instanceof Map<?, ?> rawServer)) {
            throw new BusinessException("MCP 服务配置必须是对象：" + first.getKey());
        }
        Map<String, Object> serverConfig = new LinkedHashMap<>();
        rawServer.forEach((key, value) -> serverConfig.put(String.valueOf(key), value));
        return Map.entry(first.getKey(), serverConfig);
    }

    private Map<String, Object> parseMcpConfig(Mcp mcp) {
        String configJson = mcp == null ? null : mcp.getConfigJson();
        if (configJson == null || configJson.isBlank()) {
            throw new BusinessException("MCP 配置 JSON 不能为空");
        }
        try {
            return objectMapper.readValue(configJson, new com.fasterxml.jackson.core.type.TypeReference<>() {});
        } catch (Exception e) {
            throw new BusinessException("MCP 配置 JSON 格式错误");
        }
    }

    private McpTransportType parseMcpTransportType(Mcp mcp, Map<String, Object> serverConfig) {
        String transport = firstNonBlank(mcp == null ? null : mcp.getTransportType(), stringValue(serverConfig, "transport", null));
        return McpTransportType.from(transport);
    }

    private String mcpRuntimeName(Mcp mcp) {
        BigInteger id = mcp == null ? null : mcp.getId();
        return "mcp_" + safeToolNameSegment(id == null ? "unknown" : String.valueOf(id));
    }

    private String mcpRuntimeToolPrefix(BigInteger mcpId) {
        return "mcp_" + safeToolNameSegment(String.valueOf(mcpId)) + "_";
    }

    private String safeToolNameSegment(String value) {
        String normalized = String.valueOf(value == null ? "" : value).trim()
                .replaceAll("[^A-Za-z0-9_-]", "_")
                .replaceAll("_+", "_");
        return normalized.isBlank() ? "tool" : normalized;
    }

    private List<String> stringListValue(Map<String, Object> map, String key) {
        Object value = map == null ? null : map.get(key);
        if (value == null) {
            return new ArrayList<>();
        }
        if (value instanceof Collection<?> collection) {
            List<String> result = new ArrayList<>();
            for (Object item : collection) {
                if (item != null) {
                    result.add(String.valueOf(item));
                }
            }
            return result;
        }
        throw new BusinessException("Agent 配置字段必须是数组：" + key);
    }

    private Duration durationValue(Map<String, Object> map, String key) {
        Object value = map == null ? null : map.get(key);
        if (value == null) {
            return null;
        }
        if (value instanceof Number number) {
            return Duration.ofSeconds(number.longValue());
        }
        String text = String.valueOf(value).trim();
        if (text.isEmpty()) {
            return null;
        }
        try {
            return Duration.parse(text);
        } catch (Exception ignored) {
            try {
                return Duration.ofSeconds(Long.parseLong(text));
            } catch (NumberFormatException e) {
                throw new BusinessException("Agent 配置字段必须是秒数或 Duration：" + key);
            }
        }
    }

    private List<String> resolveMcpInputs(List<String> values) {
        if (values == null || values.isEmpty()) {
            return new ArrayList<>();
        }
        List<String> result = new ArrayList<>(values.size());
        for (String value : values) {
            result.add(resolveMcpInput(value));
        }
        return result;
    }

    private Map<String, String> resolveMcpInputMap(Map<String, String> values) {
        if (values == null || values.isEmpty()) {
            return new LinkedHashMap<>();
        }
        Map<String, String> result = new LinkedHashMap<>();
        values.forEach((key, value) -> result.put(key, resolveMcpInput(value)));
        return result;
    }

    private String resolveMcpInput(String value) {
        if (value == null || value.isBlank()) {
            return value;
        }
        Matcher matcher = MCP_INPUT_PATTERN.matcher(value);
        StringBuffer resolved = new StringBuffer();
        while (matcher.find()) {
            String inputKey = matcher.group(1);
            String resolvedValue = System.getProperty("mcp.input." + inputKey);
            if (resolvedValue == null || resolvedValue.isBlank()) {
                throw new BusinessException("MCP 输入变量未解析：" + inputKey);
            }
            matcher.appendReplacement(resolved, Matcher.quoteReplacement(resolvedValue));
        }
        matcher.appendTail(resolved);
        return resolved.toString();
    }

    private Map<String, Object> mapValue(Map<String, Object> map, String key) {
        Object value = map == null ? null : map.get(key);
        if (value == null) {
            return new LinkedHashMap<>();
        }
        if (value instanceof Map<?, ?> raw) {
            Map<String, Object> result = new LinkedHashMap<>();
            raw.forEach((rawKey, rawValue) -> result.put(String.valueOf(rawKey), rawValue));
            return result;
        }
        throw new BusinessException("Agent 配置字段必须是对象：" + key);
    }

    private Map<String, String> stringMapValue(Map<String, Object> map, String key) {
        Map<String, Object> raw = mapValue(map, key);
        Map<String, String> result = new LinkedHashMap<>();
        raw.forEach((rawKey, rawValue) -> {
            if (rawValue != null) {
                result.put(rawKey, String.valueOf(rawValue));
            }
        });
        return result;
    }

    private String stringValue(Map<String, Object> map, String key, String defaultValue) {
        Object value = map == null ? null : map.get(key);
        if (value == null) {
            return defaultValue;
        }
        String text = String.valueOf(value);
        return text.isBlank() ? defaultValue : text;
    }

    private String firstNonBlank(String first, String second) {
        return first == null || first.isBlank() ? second : first;
    }

    private BigInteger firstNonNull(BigInteger first, BigInteger second) {
        return first == null ? second : first;
    }

    private record CompiledSyncTool(AgentToolSpec spec, AgentToolInvoker invoker) {
    }

    private interface ToolCall {

        /**
         * 调用工具。
         *
         * @return 工具结果
         */
        Object invoke();
    }
}
