package tech.easyflow.ai.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Assert;
import org.junit.Test;
import tech.easyflow.ai.entity.Mcp;
import tech.easyflow.ai.entity.Plugin;
import tech.easyflow.ai.mcp.McpConnectionSnapshotFactory;
import tech.easyflow.ai.plugin.PluginConnectionSnapshotFactory;
import tech.easyflow.common.web.exceptions.BusinessException;

import java.math.BigInteger;
import java.util.Map;

/**
 * 连接资源发布快照的凭据边界测试。
 */
public class ConnectionSnapshotFactoryTest {

    private final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * MCP 快照应保留拓扑和服务端输入引用，同时拒绝复制明文凭据。
     */
    @Test
    public void mcpSnapshotShouldKeepReferencesAndRejectPlaintextCredentials() {
        McpConnectionSnapshotFactory factory = new McpConnectionSnapshotFactory(objectMapper);
        Mcp mcp = mcp("""
                {"mcpServers":{"demo":{"url":"https://mcp.example.test/api",
                "headers":{"Authorization":"${input:mcp.token}"},
                "queryParams":{"tenant":"${input:mcp.tenant}"}}}}
                """);

        Map<String, Object> snapshot = factory.snapshot(mcp);

        Assert.assertEquals(mcp.getId(), snapshot.get("id"));
        Assert.assertTrue(String.valueOf(snapshot.get("configJson")).contains("${input:mcp.token}"));
        Assert.assertNotNull(snapshot.get("configHash"));
        assertBusinessFailure(() -> factory.snapshot(mcp("""
                {"mcpServers":{"demo":{"url":"https://mcp.example.test/api",
                "headers":{"Authorization":"Bearer plaintext-secret"}}}}
                """)), "必须使用");
        assertBusinessFailure(() -> factory.snapshot(mcp("""
                {"mcpServers":{"demo":{"url":"https://mcp.example.test/api",
                "extension":{"nestedApiKey":"plaintext-secret"}}}}
                """)), "敏感配置");
    }

    /**
     * Plugin 快照应只接受服务端输入引用形式的鉴权值和私有请求头。
     */
    @Test
    public void pluginSnapshotShouldKeepReferencesAndRejectPlaintextCredentials() {
        PluginConnectionSnapshotFactory factory = new PluginConnectionSnapshotFactory(objectMapper);
        Plugin plugin = plugin("${input:plugin.token}",
                "[{\"label\":\"Authorization\",\"value\":\"${input:plugin.header}\"}]");

        Map<String, Object> snapshot = factory.snapshot(plugin);

        Assert.assertEquals("${input:plugin.token}", snapshot.get("tokenValue"));
        Assert.assertFalse(snapshot.containsKey("tenantId"));
        assertBusinessFailure(() -> factory.snapshot(plugin(
                "plaintext-secret",
                "[{\"label\":\"Authorization\",\"value\":\"${input:plugin.header}\"}]")),
                "鉴权值");
        assertBusinessFailure(() -> factory.snapshot(plugin(
                "${input:plugin.token}",
                "[{\"label\":\"X-Secret\",\"value\":\"plaintext-secret\"}]")),
                "请求头凭据");
    }

    /**
     * 创建测试 MCP。
     *
     * @param configJson MCP 配置
     * @return MCP
     */
    private Mcp mcp(String configJson) {
        Mcp mcp = new Mcp();
        mcp.setId(BigInteger.ONE);
        mcp.setTitle("测试 MCP");
        mcp.setTransportType("SSE");
        mcp.setConfigJson(configJson);
        return mcp;
    }

    /**
     * 创建测试 Plugin。
     *
     * @param tokenValue 鉴权值
     * @param headers 请求头 JSON
     * @return Plugin
     */
    private Plugin plugin(String tokenValue, String headers) {
        Plugin plugin = new Plugin();
        plugin.setId(BigInteger.TWO);
        plugin.setName("测试插件");
        plugin.setBaseUrl("https://plugin.example.test/api");
        plugin.setAuthType("apiKey");
        plugin.setPosition("headers");
        plugin.setTokenKey("Authorization");
        plugin.setTokenValue(tokenValue);
        plugin.setHeaders(headers);
        plugin.setTenantId(99L);
        return plugin;
    }

    /**
     * 断言业务校验失败且消息可定位。
     *
     * @param action 待执行动作
     * @param messageFragment 消息片段
     */
    private void assertBusinessFailure(Runnable action, String messageFragment) {
        try {
            action.run();
            Assert.fail("Expected credential validation failure");
        } catch (BusinessException exception) {
            Assert.assertTrue(exception.getMessage(), exception.getMessage().contains(messageFragment));
        }
    }
}
