"""Unit tests for WiFiService.

Tests the WiFiService business logic layer, ensuring:
- WiFi status queries
- Network scanning and connection
- Mode switching
- Saved network management
- Error handling and error codes
"""
import pytest
from unittest.mock import AsyncMock, Mock

from app.backend.services.wifi_service import WiFiService
from app.backend.managers.wifi_manager import WiFiMode, WiFiStatus, WiFiNetwork, WiFiInterface


class TestWiFiServiceStatus:
    """Test WiFi status queries."""

    @pytest.mark.asyncio
    async def test_get_status_success(self, mock_wifi_manager):
        """Test successful WiFi status retrieval."""
        # Arrange
        test_interface = WiFiInterface(
            name="wlan0",
            mac="00:11:22:33:44:55",
            is_usb=False,
            is_up=True,
            connected=False,
            ssid=None,
            ip_address="10.3.141.1"
        )
        mock_wifi_manager.get_status.return_value = WiFiStatus(
            mode=WiFiMode.AP,
            interfaces=[test_interface],
            current_ssid=None,
            current_ip=None,
            ap_ssid="dangerous-pi",
            ap_ip="10.3.141.1",
            supports_dual=False
        )

        service = WiFiService(mock_wifi_manager)

        # Act
        result = await service.get_status()

        # Assert
        assert result.success is True
        assert result.data["mode"] == "ap"
        assert len(result.data["interfaces"]) == 1
        assert result.data["interfaces"][0]["name"] == "wlan0"
        assert result.data["access_point"]["ssid"] == "dangerous-pi"
        assert result.data["supports_dual"] is False

    @pytest.mark.asyncio
    async def test_get_status_with_client_connection(self, mock_wifi_manager):
        """Test WiFi status with client connection."""
        # Arrange
        test_interface = WiFiInterface(
            name="wlan0",
            mac="00:11:22:33:44:55",
            is_usb=False,
            is_up=True,
            connected=True,
            ssid="MyNetwork",
            ip_address="192.168.1.100"
        )
        mock_wifi_manager.get_status.return_value = WiFiStatus(
            mode=WiFiMode.CLIENT,
            interfaces=[test_interface],
            current_ssid="MyNetwork",
            current_ip="192.168.1.100",
            ap_ssid="dangerous-pi",
            ap_ip="10.3.141.1",
            supports_dual=False
        )

        service = WiFiService(mock_wifi_manager)

        # Act
        result = await service.get_status()

        # Assert
        assert result.success is True
        assert result.data["mode"] == "client"
        assert result.data["client"]["ssid"] == "MyNetwork"
        assert result.data["client"]["ip"] == "192.168.1.100"

    @pytest.mark.asyncio
    async def test_get_status_exception(self, mock_wifi_manager):
        """Test WiFi status query with exception."""
        # Arrange
        mock_wifi_manager.get_status.side_effect = Exception("WiFi error")
        service = WiFiService(mock_wifi_manager)

        # Act
        result = await service.get_status()

        # Assert
        assert result.success is False
        assert result.error.code == "wifi_status_error"
        assert "WiFi error" in result.error.details


class TestWiFiServiceNetworkScanning:
    """Test network scanning."""

    @pytest.mark.asyncio
    async def test_scan_networks_success(self, mock_wifi_manager):
        """Test successful network scan."""
        # Arrange
        test_networks = [
            WiFiNetwork(
                ssid="TestNetwork",
                bssid="AA:BB:CC:DD:EE:FF",
                signal_strength=-45,
                frequency=2437,
                encrypted=True,
                in_use=False
            ),
            WiFiNetwork(
                ssid="OpenNetwork",
                bssid="11:22:33:44:55:66",
                signal_strength=-65,
                frequency=2462,
                encrypted=False,
                in_use=False
            )
        ]
        mock_wifi_manager.scan_networks.return_value = test_networks
        service = WiFiService(mock_wifi_manager)

        # Act
        result = await service.scan_networks()

        # Assert
        assert result.success is True
        assert result.data["count"] == 2
        assert len(result.data["networks"]) == 2
        assert result.data["networks"][0]["ssid"] == "TestNetwork"
        assert result.data["networks"][0]["encrypted"] is True
        assert result.data["networks"][1]["ssid"] == "OpenNetwork"
        assert result.data["networks"][1]["encrypted"] is False

    @pytest.mark.asyncio
    async def test_scan_networks_with_interface(self, mock_wifi_manager):
        """Test network scan with specific interface."""
        # Arrange
        mock_wifi_manager.scan_networks.return_value = []
        service = WiFiService(mock_wifi_manager)

        # Act
        result = await service.scan_networks(interface="wlan1")

        # Assert
        assert result.success is True
        mock_wifi_manager.scan_networks.assert_called_once_with("wlan1")

    @pytest.mark.asyncio
    async def test_scan_networks_exception(self, mock_wifi_manager):
        """Test network scan with exception."""
        # Arrange
        mock_wifi_manager.scan_networks.side_effect = Exception("Scan failed")
        service = WiFiService(mock_wifi_manager)

        # Act
        result = await service.scan_networks()

        # Assert
        assert result.success is False
        assert result.error.code == "wifi_scan_error"


class TestWiFiServiceConnection:
    """Test network connection management."""

    @pytest.mark.asyncio
    async def test_connect_success(self, mock_wifi_manager):
        """Test successful network connection."""
        # Arrange
        mock_wifi_manager.connect_to_network.return_value = True
        mock_wifi_manager.get_status.return_value = WiFiStatus(
            mode=WiFiMode.CLIENT,
            interfaces=[],
            current_ssid="TestNetwork",
            current_ip="192.168.1.100",
            ap_ssid=None,
            ap_ip=None,
            supports_dual=False
        )
        service = WiFiService(mock_wifi_manager)

        # Act
        result = await service.connect(
            ssid="TestNetwork",
            password="testpass123"
        )

        # Assert
        assert result.success is True
        assert "TestNetwork" in result.data["message"]
        assert result.data["ssid"] == "TestNetwork"
        assert result.data["ip"] == "192.168.1.100"
        mock_wifi_manager.connect_to_network.assert_called_once_with(
            ssid="TestNetwork",
            password="testpass123",
            interface=None,
            hidden=False
        )

    @pytest.mark.asyncio
    async def test_connect_open_network(self, mock_wifi_manager):
        """Test connection to open network."""
        # Arrange
        mock_wifi_manager.connect_to_network.return_value = True
        mock_wifi_manager.get_status.return_value = WiFiStatus(
            mode=WiFiMode.CLIENT,
            interfaces=[],
            current_ssid="OpenNet",
            current_ip="192.168.1.50",
            ap_ssid=None,
            ap_ip=None,
            supports_dual=False
        )
        service = WiFiService(mock_wifi_manager)

        # Act
        result = await service.connect(ssid="OpenNet", password=None)

        # Assert
        assert result.success is True
        mock_wifi_manager.connect_to_network.assert_called_once_with(
            ssid="OpenNet",
            password=None,
            interface=None,
            hidden=False
        )

    @pytest.mark.asyncio
    async def test_connect_failure(self, mock_wifi_manager):
        """Test failed network connection."""
        # Arrange
        mock_wifi_manager.connect_to_network.return_value = False
        service = WiFiService(mock_wifi_manager)

        # Act
        result = await service.connect(ssid="BadNetwork", password="wrong")

        # Assert
        assert result.success is False
        assert result.error.code == "connection_failed"

    @pytest.mark.asyncio
    async def test_disconnect_success(self, mock_wifi_manager):
        """Test successful disconnect."""
        # Arrange
        mock_wifi_manager.disconnect_from_network.return_value = True
        service = WiFiService(mock_wifi_manager)

        # Act
        result = await service.disconnect()

        # Assert
        assert result.success is True
        assert "Disconnected" in result.data["message"]

    @pytest.mark.asyncio
    async def test_disconnect_failure(self, mock_wifi_manager):
        """Test failed disconnect."""
        # Arrange
        mock_wifi_manager.disconnect_from_network.return_value = False
        service = WiFiService(mock_wifi_manager)

        # Act
        result = await service.disconnect()

        # Assert
        assert result.success is False
        assert result.error.code == "disconnect_failed"


class TestWiFiServiceModeSwitch:
    """Test WiFi mode switching."""

    @pytest.mark.asyncio
    async def test_set_mode_ap(self, mock_wifi_manager):
        """Test switching to AP mode."""
        # Arrange
        mock_wifi_manager.set_mode.return_value = True
        service = WiFiService(mock_wifi_manager)

        # Act
        result = await service.set_mode("ap")

        # Assert
        assert result.success is True
        assert result.data["mode"] == "ap"

    @pytest.mark.asyncio
    async def test_set_mode_client(self, mock_wifi_manager):
        """Test switching to client mode."""
        # Arrange
        mock_wifi_manager.set_mode.return_value = True
        service = WiFiService(mock_wifi_manager)

        # Act
        result = await service.set_mode("client")

        # Assert
        assert result.success is True
        assert result.data["mode"] == "client"

    @pytest.mark.asyncio
    async def test_set_mode_invalid(self, mock_wifi_manager):
        """Test invalid mode."""
        # Arrange
        service = WiFiService(mock_wifi_manager)

        # Act
        result = await service.set_mode("invalid_mode")

        # Assert
        assert result.success is False
        assert result.error.code == "invalid_mode"

    @pytest.mark.asyncio
    async def test_set_mode_dual_not_supported(self, mock_wifi_manager):
        """Test dual mode without USB adapter."""
        # Arrange
        mock_wifi_manager.set_mode.side_effect = ValueError("Dual mode requires USB WiFi adapter")
        service = WiFiService(mock_wifi_manager)

        # Act
        result = await service.set_mode("dual")

        # Assert
        assert result.success is False
        assert result.error.code == "mode_not_supported"


class TestWiFiServiceSavedNetworks:
    """Test saved network management."""

    @pytest.mark.asyncio
    async def test_get_saved_networks_success(self, mock_wifi_manager):
        """Test retrieving saved networks."""
        # Arrange
        saved = [
            {"id": "0", "ssid": "HomeNet", "enabled": True},
            {"id": "1", "ssid": "WorkNet", "enabled": False}
        ]
        mock_wifi_manager.get_saved_networks.return_value = saved
        service = WiFiService(mock_wifi_manager)

        # Act
        result = await service.get_saved_networks()

        # Assert
        assert result.success is True
        assert result.data["count"] == 2
        assert result.data["networks"][0]["ssid"] == "HomeNet"

    @pytest.mark.asyncio
    async def test_forget_network_success(self, mock_wifi_manager):
        """Test forgetting a saved network."""
        # Arrange
        mock_wifi_manager.forget_network.return_value = True
        service = WiFiService(mock_wifi_manager)

        # Act
        result = await service.forget_network("OldNetwork")

        # Assert
        assert result.success is True
        assert "OldNetwork" in result.data["message"]
        mock_wifi_manager.forget_network.assert_called_once_with("OldNetwork")

    @pytest.mark.asyncio
    async def test_forget_network_not_found(self, mock_wifi_manager):
        """Test forgetting non-existent network."""
        # Arrange
        mock_wifi_manager.forget_network.return_value = False
        service = WiFiService(mock_wifi_manager)

        # Act
        result = await service.forget_network("NonExistent")

        # Assert
        assert result.success is False
        assert result.error.code == "network_not_found"
