# -*- coding: utf-8 -*-
"""
EscInterrupt 单元测试
验证 Esc 键中断监听器的状态机：未启动 → 启动 → 收到 Esc → 已取消 → 停止重置。
"""
import sys
import time
from unittest.mock import patch, MagicMock
import pytest

from abyss.interrupt import EscInterrupt


class TestEscInterrupt:
    """EscInterrupt 中断监听器测试。"""

    def test_is_cancelled_initially_false(self):
        """未调用 start 前 is_cancelled 返回 False。"""
        esc = EscInterrupt()
        assert esc.is_cancelled() is False

    def test_is_cancelled_after_stop(self):
        """stop 后 is_cancelled 重置为 False。"""
        esc = EscInterrupt()
        esc.start()
        esc.stop()
        assert esc.is_cancelled() is False

    @pytest.mark.skipif(sys.platform != "win32", reason="仅 Windows")
    def test_esc_key_sets_cancelled(self):
        """模拟 Esc 键按下后 is_cancelled 返回 True。

        _listen() 用 GetAsyncKeyState 轮询，需要 mock 返回两次 0x8000
        （两次都按下），第一次触发 was_pressed=False→True 的上升沿检测。
        """
        esc = EscInterrupt()

        with patch('ctypes.windll.user32.GetAsyncKeyState', return_value=0x8000):
            esc.start()
            time.sleep(0.2)  # 等待监听线程处理（50ms 轮询）

        assert esc.is_cancelled() is True
        esc.stop()

    @pytest.mark.skipif(sys.platform != "win32", reason="仅 Windows")
    def test_non_esc_key_does_not_cancel(self):
        """模拟非 Esc 键按下后 is_cancelled 仍为 False。"""
        esc = EscInterrupt()

        with patch('ctypes.windll.user32.GetAsyncKeyState', return_value=0):
            esc.start()
            time.sleep(0.2)

        # 所有轮询返回 0 → 无 Esc 按下 → 不应取消
        assert esc.is_cancelled() is False
        esc.stop()

    def test_non_windows_no_thread_started(self):
        """非 Windows 平台 start 不启动监听线程。"""
        esc = EscInterrupt()
        with patch.object(sys, 'platform', 'linux'):
            esc.start()
        assert esc._thread is None
        esc.stop()

    @pytest.mark.skipif(sys.platform != "win32", reason="仅 Windows")
    def test_start_creates_thread(self):
        """Windows 平台 start 后 _thread 不为 None。"""
        esc = EscInterrupt()
        esc.start()
        assert esc._thread is not None
        assert esc._thread.is_alive() or esc._thread.is_alive() is False
        esc.stop()
