package com.easyagents.agent.runtime.mcp;

import com.easyagents.agent.runtime.AgentRuntimeException;
import io.modelcontextprotocol.spec.McpSchema;
import org.junit.Assert;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 测试 MCP Tool 冻结清单的稳定化与服务端预算。
 */
public class McpToolManifestTest {

    /**
     * 验证远端返回重复原始 Tool 名称时立即拒绝。
     */
    @Test
    public void shouldRejectDuplicateRawToolNames() {
        expectManifestFailure(
                () -> McpToolManifest.fromTools(List.of(tool("search", "first", smallSchema()),
                        tool("search", "second", smallSchema()))),
                "Duplicate");
    }

    /**
     * 验证 Tool 名称超出字符预算时拒绝。
     */
    @Test
    public void shouldRejectOverlongToolName() {
        String name = "n".repeat(McpToolManifest.MAX_TOOL_NAME_LENGTH + 1);

        expectManifestFailure(() -> McpToolManifest.fromTools(List.of(
                tool(name, "description", smallSchema()))), "name");
    }

    /**
     * 验证 Tool 描述超出字符预算时拒绝。
     */
    @Test
    public void shouldRejectOverlongToolDescription() {
        String description = "d".repeat(McpToolManifest.MAX_TOOL_DESCRIPTION_LENGTH + 1);

        expectManifestFailure(() -> McpToolManifest.fromTools(List.of(
                tool("search", description, smallSchema()))), "description");
    }

    /**
     * 验证单个输入或输出 Schema 超出 UTF-8 预算时拒绝。
     */
    @Test
    public void shouldRejectOversizedSingleSchema() {
        McpSchema.JsonSchema oversized = schemaWithDescription(
                "x".repeat(McpToolManifest.MAX_SCHEMA_UTF8_BYTES));

        expectManifestFailure(() -> McpToolManifest.fromTools(List.of(
                tool("search", "description", oversized))), "schema");
    }

    /**
     * 验证各 Schema 合法但规范化 Manifest 聚合超过预算时拒绝。
     */
    @Test
    public void shouldRejectOversizedAggregateManifest() {
        McpSchema.JsonSchema schema = schemaWithDescription("x".repeat(220_000));
        List<McpSchema.Tool> tools = new ArrayList<>();
        for (int index = 0; index < 10; index++) {
            tools.add(new McpSchema.Tool("tool_" + index, "tool_" + index, "description",
                    schema, null, null, null));
        }

        expectManifestFailure(() -> McpToolManifest.fromTools(tools), "manifest");
    }

    /**
     * 验证哈希入口同样拒绝反序列化后的重复名称，避免绕过发布阶段校验。
     */
    @Test
    public void shouldRejectDuplicateNamesWhenHashingFrozenManifest() {
        McpToolManifestEntry first = manifestEntry("search");
        McpToolManifestEntry second = manifestEntry("search");

        expectManifestFailure(() -> McpToolManifest.hash(List.of(first, second)), "Duplicate");
    }

    /**
     * 验证运行时完全忽略冻结白名单外新增 Tool，即使新增 Tool 超出发布清单预算。
     */
    @Test
    public void shouldIgnoreOversizedRemoteToolOutsideFrozenWhitelist() {
        McpSchema.Tool frozenTool = tool("search", "description", smallSchema());
        McpSpec spec = new McpSpec();
        spec.setName("demo");
        spec.setSkillId("skill-1");
        spec.setFrozenToolManifest(McpToolManifest.fromTools(List.of(frozenTool)));
        spec.setFrozenToolManifestHash(McpToolManifest.hash(spec.getFrozenToolManifest()));
        McpSchema.Tool extraTool = tool(
                "new_remote_tool",
                "x".repeat(McpToolManifest.MAX_TOOL_DESCRIPTION_LENGTH + 1),
                smallSchema());

        McpToolManifest.assertFrozenManifest(spec, List.of(frozenTool, extraTool));
    }

    /**
     * 构造普通 MCP Tool。
     *
     * @param name Tool 名称
     * @param description Tool 描述
     * @param schema 输入 Schema
     * @return MCP Tool
     */
    private McpSchema.Tool tool(String name, String description, McpSchema.JsonSchema schema) {
        return new McpSchema.Tool(name, name, description, schema, null, null, null);
    }

    /**
     * 构造小型合法 Schema。
     *
     * @return 合法 Schema
     */
    private McpSchema.JsonSchema smallSchema() {
        return schemaWithDescription("query");
    }

    /**
     * 构造带指定属性描述的 Schema。
     *
     * @param description 属性描述
     * @return MCP JSON Schema
     */
    private McpSchema.JsonSchema schemaWithDescription(String description) {
        return new McpSchema.JsonSchema("object",
                Map.of("value", Map.of("type", "string", "description", description)),
                List.of("value"), null, null, null);
    }

    /**
     * 构造最小冻结清单项。
     *
     * @param name Tool 名称
     * @return 冻结清单项
     */
    private McpToolManifestEntry manifestEntry(String name) {
        McpToolManifestEntry entry = new McpToolManifestEntry();
        entry.setName(name);
        entry.setDescription("description");
        entry.setInputSchema(Map.of("type", "object"));
        return entry;
    }

    /**
     * 断言清单转换抛出包含指定片段的运行时异常。
     *
     * @param action 待执行动作
     * @param messageFragment 预期错误片段
     */
    private void expectManifestFailure(Runnable action, String messageFragment) {
        try {
            action.run();
            Assert.fail("Expected MCP manifest validation failure.");
        } catch (AgentRuntimeException expected) {
            Assert.assertTrue(expected.getMessage(), expected.getMessage().contains(messageFragment));
        }
    }
}
