package com.easyagents.agent.runtime.agentscope;

import com.easyagents.agent.runtime.model.AgentGenerationOptions;
import com.easyagents.agent.runtime.model.AgentMessageContentFormat;
import com.easyagents.agent.runtime.model.AgentModelProviderType;
import com.easyagents.agent.runtime.model.AgentModelSpec;
import io.agentscope.core.formatter.openai.dto.OpenAIContentPart;
import io.agentscope.core.formatter.openai.dto.OpenAIMessage;
import io.agentscope.core.message.Base64Source;
import io.agentscope.core.message.ImageBlock;
import io.agentscope.core.message.Msg;
import io.agentscope.core.message.MsgRole;
import io.agentscope.core.message.TextBlock;
import io.agentscope.core.message.ToolResultBlock;
import io.agentscope.core.message.ToolUseBlock;
import io.agentscope.core.model.OpenAIChatModel;
import org.junit.Assert;
import org.junit.Test;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;

/**
 * Agent OpenAI Chat 消息格式兼容测试。
 */
public class AgentOpenAIChatFormatterTest {

    /**
     * 验证多轮上下文中全部纯文本消息都转换为 text 内容块数组。
     */
    @Test
    public void shouldConvertAllTextMessagesToContentParts() {
        AgentOpenAIChatFormatter formatter = new AgentOpenAIChatFormatter();
        List<OpenAIMessage> messages = formatter.format(List.of(
                Msg.builder().role(MsgRole.SYSTEM).textContent("system prompt").build(),
                Msg.builder().role(MsgRole.USER).textContent("hello").build(),
                Msg.builder().role(MsgRole.ASSISTANT).textContent("hi").build(),
                Msg.builder().role(MsgRole.USER).textContent("follow up").build()));

        assertTextContent(messages.get(0), "system prompt");
        assertTextContent(messages.get(1), "hello");
        assertTextContent(messages.get(2), "hi");
        assertTextContent(messages.get(3), "follow up");
    }

    /**
     * 验证图片加文本消息保持已有内容块数组和顺序。
     */
    @Test
    public void shouldPreserveMultimodalContentParts() {
        AgentOpenAIChatFormatter formatter = new AgentOpenAIChatFormatter();
        ImageBlock image = ImageBlock.builder()
                .source(Base64Source.builder()
                        .mediaType("image/png")
                        .data("aW1hZ2U=")
                        .build())
                .build();

        List<OpenAIMessage> messages = formatter.format(List.of(
                Msg.builder()
                        .role(MsgRole.USER)
                        .content(TextBlock.builder().text("describe").build(), image)
                        .build()));

        List<OpenAIContentPart> contentParts = messages.get(0).getContentAsList();
        Assert.assertNotNull(contentParts);
        Assert.assertEquals(2, contentParts.size());
        Assert.assertEquals("text", contentParts.get(0).getType());
        Assert.assertEquals("describe", contentParts.get(0).getText());
        Assert.assertEquals("image_url", contentParts.get(1).getType());
    }

    /**
     * 验证工具调用和工具结果的附属字段在 content 数组化后保持不变。
     */
    @Test
    public void shouldPreserveToolCallFieldsAndConvertToolResult() {
        AgentOpenAIChatFormatter formatter = new AgentOpenAIChatFormatter();
        ToolUseBlock toolUse = ToolUseBlock.builder()
                .id("call-1")
                .name("lookup")
                .input(Map.of("query", "weather"))
                .build();
        ToolResultBlock toolResult = ToolResultBlock.builder()
                .id("call-1")
                .name("lookup")
                .output(TextBlock.builder().text("sunny").build())
                .build();

        List<OpenAIMessage> messages = formatter.format(List.of(
                Msg.builder().role(MsgRole.ASSISTANT).content(toolUse).build(),
                Msg.builder().role(MsgRole.TOOL).content(toolResult).build()));

        assertTextContent(messages.get(0), "");
        Assert.assertNotNull(messages.get(0).getToolCalls());
        Assert.assertEquals(1, messages.get(0).getToolCalls().size());
        Assert.assertEquals("call-1", messages.get(0).getToolCalls().get(0).getId());
        assertTextContent(messages.get(1), "sunny");
        Assert.assertEquals("call-1", messages.get(1).getToolCallId());
    }

    /**
     * 验证专用 Formatter 的供应商规则执行后仍会统一 content 数组。
     */
    @Test
    public void shouldPreserveDeepSeekAndGlmRulesWithContentParts() {
        List<OpenAIMessage> deepSeekMessages = new AgentDeepSeekChatFormatter().format(List.of(
                Msg.builder().role(MsgRole.SYSTEM).textContent("system prompt").build()));
        Assert.assertEquals("user", deepSeekMessages.get(0).getRole());
        assertTextContent(deepSeekMessages.get(0), "system prompt");

        List<OpenAIMessage> glmMessages = new AgentGLMChatFormatter().format(List.of(
                Msg.builder().role(MsgRole.SYSTEM).textContent("system prompt").build()));
        Assert.assertEquals(2, glmMessages.size());
        Assert.assertEquals("system", glmMessages.get(0).getRole());
        Assert.assertEquals("user", glmMessages.get(1).getRole());
        assertTextContent(glmMessages.get(0), "system prompt");
        assertTextContent(glmMessages.get(1), "");
    }

    /**
     * 验证内容块数组策略会安装 EasyAgents 的 OpenAI Formatter。
     *
     * @throws Exception 反射读取 Formatter 失败时抛出
     */
    @Test
    public void textPartsPolicyShouldInstallAgentOpenAIFormatter() throws Exception {
        AgentModelSpec spec = new AgentModelSpec();
        spec.setProviderType(AgentModelProviderType.OPENAI_COMPATIBLE);
        spec.setModelName("vlm-test");
        spec.setBaseUrl("http://model.example.com/v1");
        spec.setApiKey("test-key");
        spec.setMessageContentFormat(AgentMessageContentFormat.TEXT_PARTS);

        OpenAIChatModel model = (OpenAIChatModel) new AgentScopeModelFactory()
                .create(spec, new AgentGenerationOptions());
        Field formatterField = OpenAIChatModel.class.getDeclaredField("formatter");
        formatterField.setAccessible(true);

        Assert.assertTrue(formatterField.get(model) instanceof AgentOpenAIChatFormatter);
    }

    /**
     * 验证默认策略继续使用 AgentScope 原生 Formatter。
     *
     * @throws Exception 反射读取 Formatter 失败时抛出
     */
    @Test
    public void standardPolicyShouldKeepDefaultOpenAIFormatter() throws Exception {
        AgentModelSpec spec = new AgentModelSpec();
        spec.setProviderType(AgentModelProviderType.OPENAI_COMPATIBLE);
        spec.setModelName("chat-test");
        spec.setBaseUrl("http://model.example.com/v1");
        spec.setApiKey("test-key");

        OpenAIChatModel model = (OpenAIChatModel) new AgentScopeModelFactory()
                .create(spec, new AgentGenerationOptions());
        Field formatterField = OpenAIChatModel.class.getDeclaredField("formatter");
        formatterField.setAccessible(true);

        Assert.assertFalse(formatterField.get(model) instanceof AgentOpenAIChatFormatter);
    }

    /**
     * 验证严格数组策略为专用 Provider 安装保留供应商规则的 Formatter。
     *
     * @throws Exception 反射读取 Formatter 失败时抛出
     */
    @Test
    public void textPartsPolicyShouldInstallProviderSpecificFormatters() throws Exception {
        Assert.assertTrue(formatterFor(AgentModelProviderType.DEEPSEEK)
                instanceof AgentDeepSeekChatFormatter);
        Assert.assertTrue(formatterFor(AgentModelProviderType.GLM)
                instanceof AgentGLMChatFormatter);
    }

    /**
     * 创建指定 Provider 的严格数组模型并读取其 Formatter。
     *
     * @param providerType Provider 类型
     * @return 模型 Formatter
     * @throws Exception 反射读取 Formatter 失败时抛出
     */
    private Object formatterFor(AgentModelProviderType providerType) throws Exception {
        AgentModelSpec spec = new AgentModelSpec();
        spec.setProviderType(providerType);
        spec.setModelName("provider-test");
        spec.setApiKey("test-key");
        spec.setMessageContentFormat(AgentMessageContentFormat.TEXT_PARTS);

        OpenAIChatModel model = (OpenAIChatModel) new AgentScopeModelFactory()
                .create(spec, new AgentGenerationOptions());
        Field formatterField = OpenAIChatModel.class.getDeclaredField("formatter");
        formatterField.setAccessible(true);
        return formatterField.get(model);
    }

    /**
     * 断言消息只包含一个指定文本内容块。
     *
     * @param message OpenAI 请求消息
     * @param expectedText 预期文本
     */
    private void assertTextContent(OpenAIMessage message, String expectedText) {
        Assert.assertTrue(message.getContent() instanceof List<?>);
        List<OpenAIContentPart> contentParts = message.getContentAsList();
        Assert.assertNotNull(contentParts);
        Assert.assertEquals(1, contentParts.size());
        Assert.assertEquals("text", contentParts.get(0).getType());
        Assert.assertEquals(expectedText, contentParts.get(0).getText());
    }
}
