"""AgentLoop 已持久化用户轮次入口的回归测试。"""
from __future__ import annotations

import unittest
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import MagicMock
from unittest.mock import patch
from uuid import uuid4

from core.loop import AgentLoop


class _Session:
    def __init__(self, messages=None):
        self.messages = list(messages or [])
        self.appended = []
        self.append_artifacts = []

    def append(self, message, *, artifact_refs=None):
        self.messages.append(message)
        self.appended.append(message)
        self.append_artifacts.append(artifact_refs)
        return uuid4()


def _loop(session: _Session) -> AgentLoop:
    loop = AgentLoop(
        llm=MagicMock(),
        executor=MagicMock(),
        session=session,
        capabilities=SimpleNamespace(max_iterations=1),
        user_id=uuid4(),
        working_dir=Path("."),
        cancel_check=lambda: True,
    )
    loop._maybe_fold_context = MagicMock()
    return loop


class PersistedTurnTests(unittest.TestCase):
    def test_persisted_turn_does_not_append_duplicate_user_message(self) -> None:
        session = _Session([{"role": "user", "content": "已落库"}])
        result = _loop(session).run_persisted_turn()

        self.assertEqual(result, "[cancelled]")
        self.assertEqual(session.appended, [])
        self.assertEqual(session.messages, [{"role": "user", "content": "已落库"}])

    def test_persisted_turn_requires_latest_user_message(self) -> None:
        session = _Session([{"role": "assistant", "content": "旧回复"}])

        with self.assertRaisesRegex(RuntimeError, "latest message"):
            _loop(session).run_persisted_turn()

    def test_legacy_run_still_appends_user_message(self) -> None:
        session = _Session()
        result = _loop(session).run("新消息")

        self.assertEqual(result, "[cancelled]")
        self.assertEqual(
            session.appended,
            [{"role": "user", "content": "新消息"}],
        )

    def test_final_assistant_persists_explicit_artifact_list(self) -> None:
        session = _Session([{"role": "user", "content": "生成报告"}])
        loop = AgentLoop(
            llm=MagicMock(),
            executor=MagicMock(),
            session=session,
            capabilities=SimpleNamespace(
                max_iterations=1, family="test", variant="model",
                input_cny_per_mtoken=0, output_cny_per_mtoken=0,
                cache_hit_cny_per_mtoken=0,
            ),
            user_id=uuid4(),
            working_dir=Path("."),
        )
        loop._maybe_fold_context = MagicMock()
        loop._pending_artifact_refs = [{
            "version": 1, "scope": "working_dir", "path": "report.pdf",
        }]
        # _run resets turn state; emulate a published tool by restoring the pending ref when
        # the final model response is received.
        response = SimpleNamespace(
            choices=[SimpleNamespace(message=SimpleNamespace(
                content="已完成", tool_calls=None,
            ))],
            usage=None,
        )
        loop._stream_llm = MagicMock(return_value=(response, False))
        original_fold = loop._maybe_fold_context
        original_fold.side_effect = lambda: loop._pending_artifact_refs.append({
            "version": 1, "scope": "working_dir", "path": "report.pdf",
        })
        with patch("core.loop.record_chat_usage"):
            result = loop.run_persisted_turn()
        self.assertEqual(result, "已完成")
        self.assertEqual(session.append_artifacts[-1], [{
            "version": 1, "scope": "working_dir", "path": "report.pdf",
        }])


if __name__ == "__main__":
    unittest.main()
