#!/usr/bin/env python3
"""Unit tests for frustration detection state machine.

This test does not call the real side model; it mocks the semantic check
calls to verify that frustration scores, pending flags, and notifications
behave as expected.
"""

import os
import sys
import unittest

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from brain import Brain


class _FakePanel:
    def __init__(self):
        self.issues = []
        self.rags = []

    def add_issue(self, *args, **kwargs):
        self.issues.append((args, kwargs))

    def add_rag(self, *args, **kwargs):
        self.rags.append((args, kwargs))

    def add_intervention(self, *args, **kwargs):
        pass

    def is_repeated(self, *args, **kwargs):
        return False, []

    def find_related(self, *args, **kwargs):
        return []

    def save(self):
        pass

    def render_html(self):
        pass


class TestFrustrationDetection(unittest.TestCase):
    def setUp(self):
        self.notifications = []
        self.brain = Brain(
            config_path="config.yaml",
            panel=_FakePanel(),
            notify_callback=lambda msg: self.notifications.append(msg),
        )
        # Disable the real-time scan so tests do not hit the side model.
        self.brain.config.setdefault("copilot", {}).setdefault("real_time_scan", {})["enabled"] = False
        # Remove cooldown between frustration checks so tests can fire repeatedly.
        self.brain.config.setdefault("copilot", {}).setdefault("frustration_detection", {})[
            "cooldown_seconds"
        ] = 0
        # Start with a clean state in case config carries defaults.
        self.brain._reset_frustration_state()

    def _mock_frustration_check(self, score: int):
        def _invoke(text: str):
            if score > 0:
                return {"is_frustrated": True, "score": score, "reason": "mock"}
            return {"is_frustrated": False, "score": 0, "reason": ""}
        self.brain._invoke_frustration_check = _invoke

    def test_no_frustration_by_default(self):
        self._mock_frustration_check(0)
        self.brain.feed_user_input("帮我写一个读取 config.yaml 的函数\n")
        self.assertEqual(self.brain.frustration_score, 0)
        self.assertFalse(self.brain.pending_frustration_summary)
        self.assertEqual(len(self.notifications), 0)

    def test_frustration_score_increments(self):
        self._mock_frustration_check(4)
        self.brain.feed_user_input("这怎么还不行\n")
        self.assertEqual(self.brain.frustration_score, 4)

    def test_pending_flag_and_notification(self):
        # Threshold in config.yaml is 6.
        self._mock_frustration_check(6)
        self.brain.feed_user_input("搞了这么久还是错的\n")
        self.assertTrue(self.brain.pending_frustration_summary)
        self.assertTrue(any("不太满意" in n for n in self.notifications))

    def test_accumulation_reaches_threshold(self):
        self._mock_frustration_check(4)
        self.brain.feed_user_input("有点烦\n")
        self.assertFalse(self.brain.pending_frustration_summary)
        self.brain.feed_user_input("还是不对\n")
        self.assertTrue(self.brain.pending_frustration_summary)

    def test_resolution_triggers_summary_call(self):
        self._mock_frustration_check(8)
        self.brain.feed_user_input("你到底行不行\n")

        called = []
        original = self.brain._invoke_frustration_rag_summary

        def _mock():
            called.append(1)
            self.brain._reset_frustration_state()

        self.brain._invoke_frustration_rag_summary = _mock
        try:
            self.brain.feed_kimi_output("搞定了，现在应该可以了\n")
            self.assertEqual(len(called), 1)
        finally:
            self.brain._invoke_frustration_rag_summary = original

    def test_no_summary_without_frustration(self):
        self._mock_frustration_check(0)
        called = []
        self.brain._invoke_frustration_rag_summary = lambda: called.append(1)
        self.brain.feed_kimi_output("搞定了，现在应该可以了\n")
        self.assertEqual(len(called), 0)

    def test_handle_rag_result_parses_and_writes(self):
        written = []
        self.brain._write_rag = lambda entry: written.append(entry)
        self.brain.last_frustration_summary_time = 0.0

        sample = """
        {
          "worth_rag": true,
          "rag_entry": {
            "title": "读取 YAML 正确方式",
            "question": "如何读取 config.yaml",
            "wrong_approach": "open().read() 只能读字符串",
            "correct_approach": "使用 yaml.safe_load()",
            "tags": ["python", "yaml"]
          }
        }
        """
        self.brain._handle_frustration_rag_result(sample)
        self.assertEqual(len(written), 1)
        self.assertEqual(written[0]["title"], "读取 YAML 正确方式")
        self.assertTrue(any("自动总结踩坑经验" in n for n in self.notifications))


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