"""U3 / G8 delta_flush_interval 调速测试。

覆盖 R11-R12, R14:
- R11 chunk 按 flush_interval_ms 间隔批量 yield
- R12 配置化(flush_interval_ms=0 退化为逐 chunk yield)
- R14 自检:合并 content 等于原始 chunks 拼接(不丢字符)
"""

from __future__ import annotations



from agentkit.core.react import ReActEngine
from agentkit.llm.protocol import StreamChunk


class _StubGateway:
    """模拟 LLMGateway,yield 一串 StreamChunk 后结束。"""

    def __init__(self, chunks: list[str]):
        self._chunks = chunks

    def get_provider_name_for_model(self, model: str) -> str | None:
        return None

    async def chat_stream(self, **kwargs):
        for c in self._chunks:
            yield StreamChunk(content=c, model="test")


def _collect_token_events(events) -> list[str]:
    return [e.data["content"] for e in events if e.event_type == "token"]


# ---- R12 Config: flush_interval_ms=0 → 逐 chunk yield(向后兼容) ----


async def test_flush_interval_zero_yields_per_chunk():
    chunks = ["H", "e", "l", "l", "o"]
    gw = _StubGateway(chunks)
    engine = ReActEngine(llm_gateway=gw, flush_interval_ms=0)
    events = []
    async for ev in engine.execute_stream(
        messages=[{"role": "user", "content": "hi"}],
        tools=[],
        model="test",
    ):
        events.append(ev)
    tokens = _collect_token_events(events)
    # 5 chunks → 5 token events(每个内容 = 单 chunk)
    assert tokens == ["H", "e", "l", "l", "o"]


# ---- R11 Happy path: flush_interval_ms > 0 → 批量合并 ----


async def test_flush_interval_batches_chunks_by_interval():
    chunks = ["a", "b", "c", "d", "e", "f"]
    gw = _StubGateway(chunks)
    # 间隔设很大(10s),所有 chunks 在第一个 interval 内累积,流结束后最终 flush
    engine = ReActEngine(llm_gateway=gw, flush_interval_ms=10000)
    events = []
    async for ev in engine.execute_stream(
        messages=[{"role": "user", "content": "hi"}],
        tools=[],
        model="test",
    ):
        events.append(ev)
    tokens = _collect_token_events(events)
    # 所有 chunk 累积到流结束,最终 flush 一次 → 1 个 token event,content = 全拼接
    assert len(tokens) == 1
    assert tokens[0] == "abcdef"


# ---- R14 Self-check: 合并 content 等于原始 chunks 拼接(不丢字符) ----


async def test_no_character_loss_after_merge():
    chunks = ["Hello", " ", "World", "!", "你好", "世界"]
    gw = _StubGateway(chunks)
    engine = ReActEngine(llm_gateway=gw, flush_interval_ms=10000)
    events = []
    async for ev in engine.execute_stream(
        messages=[{"role": "user", "content": "hi"}],
        tools=[],
        model="test",
    ):
        events.append(ev)
    tokens = _collect_token_events(events)
    # 合并所有 token events 的 content 等于原始 chunks 拼接
    merged = "".join(tokens)
    assert merged == "".join(chunks) == "Hello World!你好世界"


# ---- Edge: 流结束 mid-interval → 最终 flush 剩余 buffer ----


async def test_final_flush_on_stream_end():
    chunks = ["x", "y", "z"]
    gw = _StubGateway(chunks)
    engine = ReActEngine(llm_gateway=gw, flush_interval_ms=10000)
    events = []
    async for ev in engine.execute_stream(
        messages=[{"role": "user", "content": "hi"}],
        tools=[],
        model="test",
    ):
        events.append(ev)
    tokens = _collect_token_events(events)
    # mid-interval 累积 → 流结束最终 flush 一次
    assert tokens == ["xyz"]


# ---- Edge: 单个 chunk 后流结束 → 立即 flush ----


async def test_single_chunk_immediate_flush():
    chunks = ["only"]
    gw = _StubGateway(chunks)
    engine = ReActEngine(llm_gateway=gw, flush_interval_ms=10000)
    events = []
    async for ev in engine.execute_stream(
        messages=[{"role": "user", "content": "hi"}],
        tools=[],
        model="test",
    ):
        events.append(ev)
    tokens = _collect_token_events(events)
    assert tokens == ["only"]


# ---- Edge: chunks 含空 content(usage-only chunk)不进 buffer ----


async def test_empty_content_chunk_not_buffered():
    chunks = ["a", "", "b"]  # 中间 chunk 空
    gw = _StubGateway(chunks)
    engine = ReActEngine(llm_gateway=gw, flush_interval_ms=10000)
    events = []
    async for ev in engine.execute_stream(
        messages=[{"role": "user", "content": "hi"}],
        tools=[],
        model="test",
    ):
        events.append(ev)
    tokens = _collect_token_events(events)
    # 空 chunk 跳过 buffer,最终 flush "ab"
    assert tokens == ["ab"]
