package tech.easyflow.agent.runtime;

import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.web.server.ResponseStatusException;
import tech.easyflow.agent.entity.Agent;
import tech.easyflow.ai.entity.Model;
import tech.easyflow.ai.service.ModelService;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;

/**
 * Agent 正式与草稿聊天图片能力边界测试。
 */
public class AgentRunServiceImageCapabilityTest {

    /**
     * 验证正式聊天允许快照已启用的图片能力，且不读取实时模型能力。
     *
     * @throws Exception 反射调用失败时抛出
     */
    @Test
    public void formalChatShouldUseEnabledPublishedCapability() throws Exception {
        ModelService modelService = Mockito.mock(ModelService.class);
        AgentRunService service = service(modelService);
        Agent agent = publishedAgent(true);

        invokeAssertImageCapability(service, agent);

        Mockito.verifyNoInteractions(modelService);
    }

    /**
     * 验证正式聊天拒绝快照未启用的图片能力，且不读取实时模型能力。
     *
     * @throws Exception 反射调用失败时抛出
     */
    @Test
    public void formalChatShouldRejectDisabledPublishedCapability() throws Exception {
        ModelService modelService = Mockito.mock(ModelService.class);
        AgentRunService service = service(modelService);
        Agent agent = publishedAgent(false);

        ResponseStatusException error = expectImageCapabilityError(service, agent);

        Assert.assertEquals(400, error.getStatusCode().value());
        Mockito.verifyNoInteractions(modelService);
    }

    /**
     * 验证旧快照缺少图片能力字段时安全拒绝图片。
     *
     * @throws Exception 反射调用失败时抛出
     */
    @Test
    public void formalChatShouldFailClosedForLegacySnapshot() throws Exception {
        ModelService modelService = Mockito.mock(ModelService.class);
        AgentRunService service = service(modelService);
        Agent agent = new Agent();
        agent.setModelId(BigInteger.TEN);
        agent.setPublishedSnapshotJson(Map.of(
                "modelSummary", Map.of("modelName", "legacy-model")));

        ResponseStatusException error = expectImageCapabilityError(service, agent);

        Assert.assertEquals(400, error.getStatusCode().value());
        Mockito.verifyNoInteractions(modelService);
    }

    /**
     * 验证草稿聊天继续读取实时模型图片能力。
     *
     * @throws Exception 反射调用失败时抛出
     */
    @Test
    public void draftChatShouldUseLiveModelCapability() throws Exception {
        Model model = new Model();
        model.setSupportImage(true);
        ModelService modelService = Mockito.mock(ModelService.class);
        Mockito.when(modelService.getModelInstance(BigInteger.TEN)).thenReturn(model);
        AgentRunService service = service(modelService);
        Agent agent = new Agent();
        agent.setModelId(BigInteger.TEN);

        invokeAssertImageCapability(service, agent);

        Mockito.verify(modelService).getModelInstance(BigInteger.TEN);
    }

    /**
     * 创建已注入模型服务的运行服务。
     *
     * @param modelService 模型服务
     * @return Agent 运行服务
     * @throws Exception 字段注入失败时抛出
     */
    private AgentRunService service(ModelService modelService) throws Exception {
        AgentRunService service = new AgentRunService();
        Field field = AgentRunService.class.getDeclaredField("modelService");
        field.setAccessible(true);
        field.set(service, modelService);
        return service;
    }

    /**
     * 创建携带图片能力的正式运行 Agent。
     *
     * @param supportImage 是否支持图片
     * @return 正式运行 Agent
     */
    private Agent publishedAgent(boolean supportImage) {
        Agent agent = new Agent();
        agent.setModelId(BigInteger.TEN);
        agent.setPublishedSnapshotJson(Map.of(
                "modelSummary", Map.of(
                        "supportImage", supportImage,
                        "supportImageB64Only", false)));
        return agent;
    }

    /**
     * 调用图片能力校验。
     *
     * @param service Agent 运行服务
     * @param agent Agent 运行视图
     * @throws Exception 反射调用失败或能力校验失败时抛出
     */
    private void invokeAssertImageCapability(AgentRunService service,
                                             Agent agent) throws Exception {
        Method method = AgentRunService.class.getDeclaredMethod(
                "assertImageCapability", Agent.class, List.class);
        method.setAccessible(true);
        try {
            method.invoke(service, agent, List.of("image-upload-id"));
        } catch (InvocationTargetException error) {
            if (error.getCause() instanceof Exception cause) {
                throw cause;
            }
            throw error;
        }
    }

    /**
     * 调用图片能力校验并返回预期的业务错误。
     *
     * @param service Agent 运行服务
     * @param agent Agent 运行视图
     * @return 图片能力错误
     * @throws Exception 反射调用失败时抛出
     */
    private ResponseStatusException expectImageCapabilityError(AgentRunService service,
                                                               Agent agent) throws Exception {
        try {
            invokeAssertImageCapability(service, agent);
            Assert.fail("expected ResponseStatusException");
            return null;
        } catch (ResponseStatusException error) {
            return error;
        }
    }
}
