"""协议模块单元测试"""

import unittest
import struct
from pathlib import Path
import sys

# 添加 src 目录到 Python 路径
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))

from novnc_client.protocol import (
    RFBProtocol,
    parse_server_version,
    parse_security_types,
    parse_server_init,
    create_framebuffer_update_request,
    create_pointer_event,
    create_key_event,
    parse_framebuffer_update_header,
    get_mouse_button_mask,
    get_scroll_button_mask,
    char_to_keysym,
    get_special_key,
)
from novnc_client.types import PixelFormat, ServerInfo, Rectangle
from novnc_client.exceptions import ProtocolError


class TestRFBProtocol(unittest.TestCase):
    """RFB 协议常量测试"""

    def test_constants(self):
        """测试常量定义"""
        self.assertEqual(RFBProtocol.VERSION, b"RFB 003.008\n")
        self.assertEqual(RFBProtocol.SECURITY_NONE, 1)
        self.assertEqual(RFBProtocol.CLIENT_FRAMEBUFFER_UPDATE_REQUEST, 3)
        self.assertEqual(RFBProtocol.SERVER_FRAMEBUFFER_UPDATE, 0)


class TestProtocolParsers(unittest.TestCase):
    """协议解析函数测试"""

    def test_parse_server_version(self):
        """测试服务器版本解析"""
        version_data = b"RFB 003.008\n"
        result = parse_server_version(version_data)
        self.assertEqual(result, "RFB 003.008")

        # 测试无效版本
        with self.assertRaises(ProtocolError):
            parse_server_version(b"INVALID")

    def test_parse_security_types(self):
        """测试安全类型解析"""
        # 正常情况
        data = bytes([2, 1, 2])  # 2个安全类型: NONE(1), VNC_AUTH(2)
        result = parse_security_types(data)
        self.assertEqual(result, [1, 2])

        # 连接失败情况
        with self.assertRaises(ProtocolError):
            failure_data = bytes([0]) + struct.pack(">I", 6) + b"Failed"
            parse_security_types(failure_data)

        # 数据不足
        with self.assertRaises(ProtocolError):
            parse_security_types(b"")

    def test_parse_server_init(self):
        """测试服务器初始化解析"""
        # 构造测试数据
        width = 1280
        height = 800
        pixel_format = (
            struct.pack(">BBBBHHHBBB", 32, 24, 0, 1, 255, 255, 255, 16, 8, 0)
            + b"\x00" * 3
        )
        name = b"Test Server"
        name_length = len(name)

        data = (
            struct.pack(">HH", width, height)
            + pixel_format
            + struct.pack(">I", name_length)
            + name
        )

        result = parse_server_init(data)

        self.assertIsInstance(result, ServerInfo)
        self.assertEqual(result.width, width)
        self.assertEqual(result.height, height)
        self.assertEqual(result.server_name, "Test Server")
        self.assertIsInstance(result.pixel_format, PixelFormat)

        # 测试数据不足
        with self.assertRaises(ProtocolError):
            parse_server_init(b"short data")


class TestMessageCreators(unittest.TestCase):
    """消息创建函数测试"""

    def test_create_framebuffer_update_request(self):
        """测试帧缓冲区更新请求创建"""
        message = create_framebuffer_update_request(True, 10, 20, 100, 200)
        expected = struct.pack(">BBHHHH", 3, 1, 10, 20, 100, 200)
        self.assertEqual(message, expected)

    def test_create_pointer_event(self):
        """测试鼠标事件创建"""
        message = create_pointer_event(100, 200, 1)
        expected = struct.pack(">BBHH", 5, 1, 100, 200)
        self.assertEqual(message, expected)

    def test_create_key_event(self):
        """测试键盘事件创建"""
        message = create_key_event(65, True)  # 'A' key down
        expected = struct.pack(">BBBBI", 4, 1, 0, 0, 65)
        self.assertEqual(message, expected)


class TestButtonMasks(unittest.TestCase):
    """按钮掩码测试"""

    def test_get_mouse_button_mask(self):
        """测试鼠标按钮掩码"""
        self.assertEqual(get_mouse_button_mask("left"), 1)
        self.assertEqual(get_mouse_button_mask("right"), 4)
        self.assertEqual(get_mouse_button_mask("middle"), 2)
        self.assertEqual(get_mouse_button_mask("invalid"), 0)

    def test_get_scroll_button_mask(self):
        """测试滚轮按钮掩码"""
        self.assertEqual(get_scroll_button_mask("up"), 8)
        self.assertEqual(get_scroll_button_mask("down"), 16)
        self.assertEqual(get_scroll_button_mask("invalid"), 0)


class TestKeyboardMapping(unittest.TestCase):
    """键盘映射测试"""

    def test_char_to_keysym(self):
        """测试字符到键码转换"""
        # ASCII 字符
        self.assertEqual(char_to_keysym("A"), 65)
        self.assertEqual(char_to_keysym("a"), 97)
        self.assertEqual(char_to_keysym("1"), 49)
        self.assertEqual(char_to_keysym(" "), 32)

        # 特殊字符
        self.assertEqual(char_to_keysym("\n"), RFBProtocol.KEY_RETURN)
        self.assertEqual(char_to_keysym("\t"), RFBProtocol.KEY_TAB)
        self.assertEqual(char_to_keysym("\b"), RFBProtocol.KEY_BACKSPACE)

        # 无效输入
        with self.assertRaises(ValueError):
            char_to_keysym("abc")  # 多个字符

    def test_get_special_key(self):
        """测试特殊按键获取"""
        self.assertEqual(get_special_key("enter"), RFBProtocol.KEY_RETURN)
        self.assertEqual(
            get_special_key("ENTER"), RFBProtocol.KEY_RETURN
        )  # 大小写不敏感
        self.assertEqual(get_special_key("ctrl"), RFBProtocol.KEY_CTRL_L)
        self.assertEqual(get_special_key("shift"), RFBProtocol.KEY_SHIFT_L)
        self.assertEqual(get_special_key("alt"), RFBProtocol.KEY_ALT_L)
        self.assertEqual(get_special_key("win"), RFBProtocol.KEY_WIN_L)
        self.assertEqual(get_special_key("f1"), RFBProtocol.KEY_F1)
        self.assertEqual(get_special_key("left"), RFBProtocol.KEY_LEFT)

        # 无效按键
        with self.assertRaises(ValueError):
            get_special_key("invalid_key")


class TestFramebufferUpdate(unittest.TestCase):
    """帧缓冲区更新测试"""

    def test_parse_framebuffer_update_header(self):
        """测试帧缓冲区更新头部解析"""
        # 构造测试数据: 消息类型(1) + padding(1) + 矩形数量(2) + 矩形数据(12)
        rect_count = 1
        x, y, width, height, encoding = 10, 20, 100, 200, 0

        data = struct.pack(">BBHHHHHI", 0, 0, rect_count, x, y, width, height, encoding)

        count, rectangles = parse_framebuffer_update_header(data)

        self.assertEqual(count, rect_count)
        self.assertEqual(len(rectangles), 1)

        rect = rectangles[0]
        self.assertIsInstance(rect, Rectangle)
        self.assertEqual(rect.x, x)
        self.assertEqual(rect.y, y)
        self.assertEqual(rect.width, width)
        self.assertEqual(rect.height, height)
        self.assertEqual(rect.encoding, encoding)

        # 测试数据不足
        with self.assertRaises(ProtocolError):
            parse_framebuffer_update_header(b"123")


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