"""
Tests for AdbDeviceManager
"""

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

import pytest

from adbdevicemanager import AdbDeviceManager

# Add the parent directory to the path so we can import our modules
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))


class TestAdbDeviceManager:
    """Test AdbDeviceManager functionality"""

    @patch('adbdevicemanager.AdbDeviceManager.check_adb_installed')
    @patch('adbdevicemanager.AdbDeviceManager.get_available_devices')
    @patch('adbdevicemanager.AdbClient')
    def test_single_device_auto_selection(self, mock_adb_client, mock_get_devices, mock_check_adb):
        """Test auto-selection when only one device is connected"""
        # Setup mocks
        mock_check_adb.return_value = True
        mock_get_devices.return_value = ["device123"]
        mock_device = MagicMock()
        mock_adb_client.return_value.device.return_value = mock_device

        # Test with device_name=None (auto-selection)
        with patch('builtins.print') as mock_print:
            manager = AdbDeviceManager(device_name=None, exit_on_error=False)

            # Verify the correct device was selected
            mock_adb_client.return_value.device.assert_called_once_with(
                "device123")
            assert manager.device == mock_device

            # Verify auto-selection message was printed
            mock_print.assert_called_with(
                "No device specified, automatically selected: device123")

    @patch('adbdevicemanager.AdbDeviceManager.check_adb_installed')
    @patch('adbdevicemanager.AdbDeviceManager.get_available_devices')
    def test_multiple_devices_no_selection_error(self, mock_get_devices, mock_check_adb):
        """Test error when multiple devices are connected but none specified"""
        # Setup mocks
        mock_check_adb.return_value = True
        mock_get_devices.return_value = ["device123", "device456"]

        # Test with device_name=None and multiple devices
        with pytest.raises(RuntimeError) as exc_info:
            AdbDeviceManager(device_name=None, exit_on_error=False)

        assert "Multiple devices connected" in str(exc_info.value)
        assert "device123" in str(exc_info.value)
        assert "device456" in str(exc_info.value)

    @patch('adbdevicemanager.AdbDeviceManager.check_adb_installed')
    @patch('adbdevicemanager.AdbDeviceManager.get_available_devices')
    @patch('adbdevicemanager.AdbClient')
    def test_specific_device_selection(self, mock_adb_client, mock_get_devices, mock_check_adb):
        """Test selecting a specific device"""
        # Setup mocks
        mock_check_adb.return_value = True
        mock_get_devices.return_value = ["device123", "device456"]
        mock_device = MagicMock()
        mock_adb_client.return_value.device.return_value = mock_device

        # Test with specific device name
        manager = AdbDeviceManager(
            device_name="device456", exit_on_error=False)

        # Verify the correct device was selected
        mock_adb_client.return_value.device.assert_called_once_with(
            "device456")
        assert manager.device == mock_device

    @patch('adbdevicemanager.AdbDeviceManager.check_adb_installed')
    @patch('adbdevicemanager.AdbDeviceManager.get_available_devices')
    def test_device_not_found_error(self, mock_get_devices, mock_check_adb):
        """Test error when specified device is not found"""
        # Setup mocks
        mock_check_adb.return_value = True
        mock_get_devices.return_value = ["device123", "device456"]

        # Test with non-existent device
        with pytest.raises(RuntimeError) as exc_info:
            AdbDeviceManager(device_name="non-existent-device",
                             exit_on_error=False)

        assert "Device non-existent-device not found" in str(exc_info.value)
        assert "Available devices" in str(exc_info.value)

    @patch('adbdevicemanager.AdbDeviceManager.check_adb_installed')
    @patch('adbdevicemanager.AdbDeviceManager.get_available_devices')
    def test_no_devices_connected_error(self, mock_get_devices, mock_check_adb):
        """Test error when no devices are connected"""
        # Setup mocks
        mock_check_adb.return_value = True
        mock_get_devices.return_value = []

        # Test with no devices
        with pytest.raises(RuntimeError) as exc_info:
            AdbDeviceManager(device_name=None, exit_on_error=False)

        assert "No devices connected" in str(exc_info.value)

    @patch('adbdevicemanager.AdbDeviceManager.check_adb_installed')
    def test_adb_not_installed_error(self, mock_check_adb):
        """Test error when ADB is not installed"""
        # Setup mocks
        mock_check_adb.return_value = False

        # Test with ADB not installed
        with pytest.raises(RuntimeError) as exc_info:
            AdbDeviceManager(device_name=None, exit_on_error=False)

        assert "adb is not installed" in str(exc_info.value)

    @patch('subprocess.run')
    def test_check_adb_installed_success(self, mock_run):
        """Test successful ADB installation check"""
        mock_run.return_value = MagicMock()  # Successful run

        result = AdbDeviceManager.check_adb_installed()

        assert result is True
        mock_run.assert_called_once()

    @patch('subprocess.run')
    def test_check_adb_installed_failure(self, mock_run):
        """Test failed ADB installation check"""
        mock_run.side_effect = FileNotFoundError()  # ADB not found

        result = AdbDeviceManager.check_adb_installed()

        assert result is False

    @patch('adbdevicemanager.AdbClient')
    def test_get_available_devices(self, mock_adb_client):
        """Test getting available devices"""
        # Setup mock devices
        mock_device1 = MagicMock()
        mock_device1.serial = "device123"
        mock_device2 = MagicMock()
        mock_device2.serial = "device456"

        mock_adb_client.return_value.devices.return_value = [
            mock_device1, mock_device2]

        devices = AdbDeviceManager.get_available_devices()

        assert devices == ["device123", "device456"]

    @patch('adbdevicemanager.AdbDeviceManager.check_adb_installed')
    @patch('adbdevicemanager.AdbDeviceManager.get_available_devices')
    @patch('adbdevicemanager.AdbClient')
    def test_exit_on_error_true(self, mock_adb_client, mock_get_devices, mock_check_adb):
        """Test that exit_on_error=True calls sys.exit"""
        # Setup mocks to trigger error
        mock_check_adb.return_value = True
        mock_get_devices.return_value = []  # No devices

        # Test with exit_on_error=True (default)
        with patch('sys.exit') as mock_exit:
            with patch('builtins.print'):  # Suppress error output
                AdbDeviceManager(device_name=None, exit_on_error=True)

            mock_exit.assert_called_once_with(1)
