package tech.easyflow.agent.runtime.agui;

import org.junit.Assert;
import org.junit.Test;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.verify;

/**
 * 可重连 AG-UI SSE 发射器测试。
 */
public class ResumableAguiSseEmitterTest {

    /**
     * 验证协议事件先进入日志，且逻辑关闭只发生在运行终态。
     */
    @Test
    public void shouldKeepLogicalRunOpenUntilExplicitCompletion() {
        AgentAguiRunJournal journal = mock(AgentAguiRunJournal.class);
        SseEmitter subscriber = new SseEmitter();
        ResumableAguiSseEmitter emitter = new ResumableAguiSseEmitter(
                "thread-1", "run-1", journal, subscriber);

        Assert.assertSame(subscriber, emitter.getEmitter());
        Assert.assertTrue(emitter.sendData("{\"type\":\"RUN_STARTED\"}"));
        Assert.assertFalse(emitter.isClosed());
        verify(journal).append("run-1", "{\"type\":\"RUN_STARTED\"}");

        emitter.complete();

        Assert.assertTrue(emitter.isClosed());
        Assert.assertTrue(emitter.isJournalCompletionSuccessful());
        verify(journal).complete("run-1");
    }

    /**
     * 验证日志拒绝普通事件时立即写入可重放故障终态并关闭逻辑输出。
     */
    @Test
    public void shouldFailRunWhenJournalRejectsEvent() {
        AgentAguiRunJournal journal = mock(AgentAguiRunJournal.class);
        doThrow(new IllegalStateException("超过 16 MiB 安全上限"))
                .when(journal).append("run-1", "{\"type\":\"RUN_STARTED\"}");
        ResumableAguiSseEmitter emitter = new ResumableAguiSseEmitter(
                "thread-1", "run-1", journal, new SseEmitter());

        Assert.assertFalse(emitter.sendData("{\"type\":\"RUN_STARTED\"}"));

        Assert.assertTrue(emitter.isClosed());
        Assert.assertFalse(emitter.isJournalCompletionSuccessful());
        verify(journal).fail(
                "thread-1", "run-1", "AGENT_RUN_JOURNAL_LIMIT",
                "Agent 输出超过可恢复日志容量，运行已停止");
    }

    /**
     * 验证终态刷盘失败会被调用方观察到，避免上层错误记录为完成态。
     */
    @Test
    public void shouldExposeTerminalJournalFailure() {
        AgentAguiRunJournal journal = mock(AgentAguiRunJournal.class);
        doThrow(new IllegalStateException("Redis unavailable"))
                .when(journal).complete("run-1");
        ResumableAguiSseEmitter emitter = new ResumableAguiSseEmitter(
                "thread-1", "run-1", journal, new SseEmitter());

        emitter.complete();

        Assert.assertTrue(emitter.isClosed());
        Assert.assertFalse(emitter.isJournalCompletionSuccessful());
        verify(journal).fail(
                "thread-1", "run-1", "AGENT_RUN_JOURNAL_UNAVAILABLE",
                "Agent 输出日志暂时不可用，运行已停止");
    }
}
