package com.easyagents.agent.runtime.event.interceptor;

import com.easyagents.agent.runtime.agentscope.AgentScopeMessageAdapter;
import com.easyagents.agent.runtime.media.AgentMediaResource;
import com.easyagents.agent.runtime.message.AgentMediaBlock;
import io.agentscope.core.ReActAgent;
import io.agentscope.core.hook.PreReasoningEvent;
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.URLSource;
import io.agentscope.core.model.ChatResponse;
import io.agentscope.core.model.GenerateOptions;
import io.agentscope.core.model.Model;
import io.agentscope.core.model.ToolSchema;
import io.agentscope.core.tool.Toolkit;
import org.junit.Assert;
import org.junit.Test;
import reactor.core.publisher.Flux;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 测试模型调用边界的媒体引用解析。
 */
public class MediaReferenceInterceptorTest {

    /**
     * 验证私有引用仅在模型调用边界转换为 Base64，原始记忆消息保持稳定引用。
     */
    @Test
    public void shouldResolveReferenceToBase64WithoutMutatingOriginalMessage() {
        AgentScopeMessageAdapter adapter = new AgentScopeMessageAdapter();
        AgentMediaBlock mediaBlock = new AgentMediaBlock("image");
        mediaBlock.setReference("draft:upload-1");
        mediaBlock.setMimeType("image/png");
        Msg original = Msg.builder()
                .id("message-1")
                .name("user")
                .role(MsgRole.USER)
                .content(adapter.toContentBlock(mediaBlock))
                .build();
        AtomicReference<String> resolvedReference = new AtomicReference<>();
        MediaReferenceInterceptor interceptor = new MediaReferenceInterceptor(reference -> {
            resolvedReference.set(reference);
            return new AgentMediaResource("image/png", "png-data".getBytes(StandardCharsets.UTF_8));
        });
        ReActAgent agent = ReActAgent.builder()
                .name("media-test-agent")
                .sysPrompt("system")
                .model(new EmptyModel())
                .toolkit(new Toolkit())
                .build();
        PreReasoningEvent event = new PreReasoningEvent(agent, "reasoning-1", null, List.of(original));

        interceptor.intercept(event).block();

        ImageBlock originalImage = original.getFirstContentBlock(ImageBlock.class);
        ImageBlock resolvedImage = event.getInputMessages().get(0).getFirstContentBlock(ImageBlock.class);
        Assert.assertTrue(originalImage.getSource() instanceof URLSource);
        Assert.assertTrue(((URLSource) originalImage.getSource()).getUrl()
                .startsWith(AgentScopeMessageAdapter.MEDIA_REFERENCE_SCHEME));
        Assert.assertTrue(resolvedImage.getSource() instanceof Base64Source);
        Assert.assertEquals("draft:upload-1", resolvedReference.get());
        Assert.assertEquals("cG5nLWRhdGE=", ((Base64Source) resolvedImage.getSource()).getData());
    }

    /**
     * 验证消息适配器序列化后仍能恢复不透明媒体引用。
     */
    @Test
    public void shouldRoundTripStableReferenceThroughMessageAdapter() {
        AgentScopeMessageAdapter adapter = new AgentScopeMessageAdapter();
        AgentMediaBlock mediaBlock = new AgentMediaBlock("image");
        mediaBlock.setReference("formal:message-1:0:image/png");

        AgentMediaBlock restored = (AgentMediaBlock) adapter.toAgentBlock(adapter.toContentBlock(mediaBlock));

        Assert.assertEquals("formal:message-1:0:image/png", restored.getReference());
        Assert.assertNull(restored.getData());
        Assert.assertNull(restored.getUrl());
    }

    /**
     * 仅用于构建真实 AgentScope 事件的空模型。
     */
    private static final class EmptyModel implements Model {

        /**
         * 返回空响应流，当前测试不会实际调用模型。
         *
         * @param messages 输入消息
         * @param toolSchemas 工具定义
         * @param options 生成参数
         * @return 空响应流
         */
        @Override
        public Flux<ChatResponse> stream(List<Msg> messages,
                                         List<ToolSchema> toolSchemas,
                                         GenerateOptions options) {
            return Flux.empty();
        }

        /**
         * 返回测试模型名称。
         *
         * @return 测试模型名称
         */
        @Override
        public String getModelName() {
            return "media-test-model";
        }
    }
}
