"""
HookExecutor 单元测试 (H31)
测试 HookExecutor 在 Frida 未连接/已连接 两种状态下的行为。
使用 Mock 模拟 FridaManager，不需要真机。
"""

import sys
import os
import unittest
from unittest.mock import MagicMock, patch

sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "agent"))

from hook.hook_executor import HookExecutor, ACTION_TO_RPC
from hook.frida_manager import FridaManager


class TestHookExecutorOffline(unittest.TestCase):
    """FridaManager 未连接时 HookExecutor 应安全返回错误"""

    def setUp(self):
        self.mgr = MagicMock(spec=FridaManager)
        self.mgr.connected = False
        self.executor = HookExecutor(self.mgr)

    def test_available_false_when_disconnected(self):
        self.assertFalse(self.executor.available)

    def test_execute_returns_error_when_disconnected(self):
        result = self.executor.execute("send_message", {"to_id": "test", "content": "hello"})
        self.assertFalse(result["success"])
        self.assertIn("不可用", result["error"])
        self.assertEqual(result["channel"], "hook")

    def test_supports_known_actions(self):
        for action in ACTION_TO_RPC:
            self.assertTrue(self.executor.supports(action), f"{action} should be supported")

    def test_supports_returns_false_for_unknown(self):
        self.assertFalse(self.executor.supports("fly_to_moon"))

    def test_get_status(self):
        self.mgr.get_status.return_value = {"connected": False, "device": None}
        status = self.executor.get_status()
        self.assertFalse(status["available"])
        self.assertIsInstance(status["supported_actions"], list)
        self.assertGreater(len(status["supported_actions"]), 10)


class TestHookExecutorOnline(unittest.TestCase):
    """FridaManager 已连接时 HookExecutor 应正确路由 RPC 调用"""

    def setUp(self):
        self.mgr = MagicMock(spec=FridaManager)
        self.mgr.connected = True
        self.mgr.call_rpc.return_value = {"success": True, "message_id": "msg_123"}
        self.executor = HookExecutor(self.mgr)

    def test_available_true_when_connected(self):
        self.assertTrue(self.executor.available)

    def test_send_message_routes_to_rpc(self):
        params = {"to_id": "wxid_abc", "content": "hello"}
        result = self.executor.execute("send_message", params)
        self.mgr.call_rpc.assert_called_once_with("sendMessage", params)
        self.assertTrue(result["success"])
        self.assertEqual(result["channel"], "hook")

    def test_get_contacts_routes_to_rpc(self):
        self.mgr.call_rpc.return_value = {"success": True, "contacts": [], "count": 0}
        result = self.executor.execute("get_contacts", {"limit": 100})
        self.mgr.call_rpc.assert_called_once_with("getContacts", {"limit": 100})
        self.assertTrue(result["success"])

    def test_unknown_action_returns_error(self):
        result = self.executor.execute("nonexistent_action", {})
        self.assertFalse(result["success"])
        self.assertIn("不支持", result["error"])

    def test_all_actions_map_to_rpc(self):
        for action, rpc_method in ACTION_TO_RPC.items():
            self.mgr.call_rpc.reset_mock()
            self.mgr.call_rpc.return_value = {"success": True}
            self.executor.execute(action, {"test": True})
            self.mgr.call_rpc.assert_called_once_with(rpc_method, {"test": True})

    def test_rpc_failure_propagates(self):
        self.mgr.call_rpc.return_value = {"success": False, "error": "微信未运行"}
        result = self.executor.execute("send_message", {"to_id": "x", "content": "y"})
        self.assertFalse(result["success"])
        self.assertEqual(result["error"], "微信未运行")

    # H19 好友管理动作
    def test_add_friend(self):
        self.mgr.call_rpc.return_value = {"success": True, "user_id": "test_user"}
        result = self.executor.execute("add_friend", {"user_id": "test_user", "message": "hi"})
        self.mgr.call_rpc.assert_called_once_with("addFriend", {"user_id": "test_user", "message": "hi"})

    def test_accept_friend(self):
        self.mgr.call_rpc.return_value = {"success": True}
        result = self.executor.execute("accept_friend", {"encrypt_username": "v3_xxx", "ticket": "t_xxx"})
        self.assertTrue(result["success"])

    # H22 群管理动作
    def test_create_group(self):
        self.mgr.call_rpc.return_value = {"success": True}
        result = self.executor.execute("create_group", {"member_ids": ["a", "b"], "topic": "测试群"})
        self.mgr.call_rpc.assert_called_once()

    def test_invite_to_group(self):
        self.mgr.call_rpc.return_value = {"success": True}
        result = self.executor.execute("invite_to_group", {"group_id": "xxx@chatroom", "member_ids": ["a"]})
        self.assertTrue(result["success"])

    # H20/H21 朋友圈动作
    def test_post_moments(self):
        self.mgr.call_rpc.return_value = {"success": True, "sns_id": "sns_123"}
        result = self.executor.execute("post_moments", {"content": "今天天气真好"})
        self.assertTrue(result["success"])

    def test_like_moments(self):
        self.mgr.call_rpc.return_value = {"success": True}
        result = self.executor.execute("like_moments", {"sns_id": "12345"})
        self.assertTrue(result["success"])


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