"""测试 emulator.os_control 模块。

由于 OS 控制依赖真实进程/命令行工具，大部分测试使用 mock。
"""

from __future__ import annotations

from unittest.mock import patch

import pytest

from autowsgr.emulator import (
    LinuxEmulatorManager,
    MacEmulatorManager,
    WindowsEmulatorManager,
)
from autowsgr.infra import EmulatorConfig, EmulatorError, EmulatorNotFoundError
from autowsgr.types import EmulatorType


# ═══════════════════════════════════════════════
# WindowsEmulatorManager
# ═══════════════════════════════════════════════


class TestWindowsEmulatorManager:
    """Windows 模拟器管理器测试。"""

    def test_yunshouji_always_running(self):
        config = EmulatorConfig(type=EmulatorType.yunshouji)
        mgr = WindowsEmulatorManager(config)
        assert mgr.is_running() is True

    def test_yunshouji_start_noop(self):
        config = EmulatorConfig(type=EmulatorType.yunshouji)
        mgr = WindowsEmulatorManager(config)
        mgr.start()  # 不应抛异常

    def test_yunshouji_stop_noop(self):
        config = EmulatorConfig(type=EmulatorType.yunshouji)
        mgr = WindowsEmulatorManager(config)
        mgr.stop()  # 不应抛异常

    def test_leidian_is_running_true(self):
        config = EmulatorConfig(
            type=EmulatorType.leidian,
            path=r'C:\LDPlayer\dnplayer.exe',
            serial='emulator-5554',
        )
        mgr = WindowsEmulatorManager(config)
        with patch.object(mgr, '_ldconsole', return_value='running'):
            assert mgr.is_running() is True

    def test_leidian_is_running_false(self):
        config = EmulatorConfig(
            type=EmulatorType.leidian,
            path=r'C:\LDPlayer\dnplayer.exe',
            serial='emulator-5554',
        )
        mgr = WindowsEmulatorManager(config)
        with patch.object(mgr, '_ldconsole', return_value='stopped'):
            assert mgr.is_running() is False

    def test_mumu_is_running_true(self):
        config = EmulatorConfig(
            type=EmulatorType.mumu,
            path=r'C:\MuMu\MuMuPlayer.exe',
            serial='127.0.0.1:16384',
        )
        mgr = WindowsEmulatorManager(config)
        json_resp = '{"is_android_started": true}'
        with patch.object(mgr, '_mumuconsole', return_value=json_resp):
            assert mgr.is_running() is True

    def test_mumu_is_running_false(self):
        config = EmulatorConfig(
            type=EmulatorType.mumu,
            path=r'C:\MuMu\MuMuPlayer.exe',
            serial='127.0.0.1:16384',
        )
        mgr = WindowsEmulatorManager(config)
        json_resp = '{"is_android_started": false}'
        with patch.object(mgr, '_mumuconsole', return_value=json_resp):
            assert mgr.is_running() is False

    def test_mumu_is_running_bad_json(self):
        config = EmulatorConfig(type=EmulatorType.mumu, path=r'C:\MuMu\x.exe')
        mgr = WindowsEmulatorManager(config)
        with patch.object(mgr, '_mumuconsole', return_value='not json'):
            assert mgr.is_running() is False

    def test_others_tasklist_check_running(self):
        config = EmulatorConfig(
            type=EmulatorType.bluestacks,
            process_name='HD-Player.exe',
        )
        mgr = WindowsEmulatorManager(config)
        fake_output = '映像名称  PID  会话名  会话#  内存使用\nHD-Player.exe  1234  Console  1  100,000 K'.encode(
            'gbk'
        )
        with patch(
            'autowsgr.emulator.os_control.windows.subprocess.check_output', return_value=fake_output
        ):
            assert mgr.is_running() is True

    def test_others_tasklist_check_not_running(self):
        config = EmulatorConfig(
            type=EmulatorType.bluestacks,
            process_name='HD-Player.exe',
        )
        mgr = WindowsEmulatorManager(config)
        fake_output = '信息: 没有运行的任务匹配'.encode('gbk')
        with patch('subprocess.check_output', return_value=fake_output):
            assert mgr.is_running() is False

    def test_start_no_path_raises(self):
        config = EmulatorConfig(type=EmulatorType.leidian, path=None)
        mgr = WindowsEmulatorManager(config)
        with pytest.raises(EmulatorNotFoundError, match='路径'):
            mgr.start()

    def test_stop_no_process_name_raises(self):
        config = EmulatorConfig(type=EmulatorType.bluestacks, process_name=None)
        mgr = WindowsEmulatorManager(config)
        with pytest.raises(EmulatorError, match='进程名'):
            mgr.stop()


# ═══════════════════════════════════════════════
# MacEmulatorManager
# ═══════════════════════════════════════════════


class TestMacEmulatorManager:
    """macOS 模拟器管理器测试。"""

    def test_is_running_no_process_name(self):
        config = EmulatorConfig(type=EmulatorType.mumu, process_name=None)
        mgr = MacEmulatorManager(config)
        assert mgr.is_running() is False

    def test_is_running_process_not_found(self):
        config = EmulatorConfig(type=EmulatorType.bluestacks, process_name='bluestacks')
        mgr = MacEmulatorManager(config)
        from subprocess import CalledProcessError

        with patch('subprocess.check_output', side_effect=CalledProcessError(1, 'pgrep')):
            assert mgr.is_running() is False

    def test_is_running_non_mumu(self):
        config = EmulatorConfig(type=EmulatorType.bluestacks, process_name='bluestacks')
        mgr = MacEmulatorManager(config)
        with patch('subprocess.check_output', return_value=b'12345'):
            assert mgr.is_running() is True

    def test_start_no_path_raises(self):
        config = EmulatorConfig(type=EmulatorType.bluestacks, path=None)
        mgr = MacEmulatorManager(config)
        with pytest.raises(EmulatorNotFoundError, match='路径'):
            mgr.start()

    def test_stop_no_process_name_raises(self):
        config = EmulatorConfig(type=EmulatorType.bluestacks, process_name=None)
        mgr = MacEmulatorManager(config)
        with pytest.raises(EmulatorError, match='进程名'):
            mgr.stop()

    def test_stop_mumu_noop(self):
        config = EmulatorConfig(type=EmulatorType.mumu, process_name='mumu')
        mgr = MacEmulatorManager(config)
        mgr.stop()  # MuMu macOS 暂不支持关闭，不应报错


# ═══════════════════════════════════════════════
# LinuxEmulatorManager
# ═══════════════════════════════════════════════


class TestLinuxEmulatorManager:
    """Linux/WSL 模拟器管理器测试。"""

    def test_start_no_path_raises(self):
        config = EmulatorConfig(type=EmulatorType.leidian, path=None)
        mgr = LinuxEmulatorManager(config)
        with pytest.raises(EmulatorNotFoundError, match='路径'):
            mgr.start()

    def test_stop_no_process_name_raises(self):
        config = EmulatorConfig(type=EmulatorType.leidian, process_name=None)
        mgr = LinuxEmulatorManager(config)
        with pytest.raises(EmulatorError, match='进程名'):
            mgr.stop()

    def test_adb_devices_empty(self):
        """_adb_devices 在异常时返回空列表。"""
        with patch(
            'autowsgr.emulator.os_control.linux.LinuxEmulatorManager._adb_devices',
            return_value=[],
        ):
            config = EmulatorConfig(
                type=EmulatorType.leidian,
                serial='emulator-5554',
                process_name='ld',
            )
            mgr = LinuxEmulatorManager(config)
            # Patch is_wsl and pgrep
            mgr._is_wsl = False
            with patch('subprocess.run') as mock_run:
                from subprocess import CalledProcessError

                mock_run.side_effect = CalledProcessError(1, 'pgrep')
                assert mgr.is_running() is False

    def test_is_running_adb_online(self):
        """设备在 ADB 列表中应返回 True。"""
        config = EmulatorConfig(
            type=EmulatorType.leidian,
            serial='emulator-5554',
        )
        mgr = LinuxEmulatorManager(config)
        with patch.object(LinuxEmulatorManager, '_adb_devices', return_value=['emulator-5554']):
            assert mgr.is_running() is True
