#!/usr/bin/env python3
"""
Unit tests for the device detection module
"""

import os
import sys
import unittest
from unittest import mock

# Add the project directory to the path so we can import the module
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

from esppal.device import DeviceDetector, list_esp_devices


class MockSerial:
    """Mock implementation of serial.Serial for testing"""
    
    def __init__(self, *args, **kwargs):
        self.is_open = True
        self.port = kwargs.get('port', args[0] if args else None)
    
    def close(self):
        self.is_open = False
        
    def __enter__(self):
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()


class MockPort:
    """Mock implementation of a serial port listing for testing"""
    
    def __init__(self, device, description="", hwid="", vid=None, pid=None):
        self.device = device
        self.description = description
        self.hwid = hwid
        self.vid = vid
        self.pid = pid


class TestDeviceDetector(unittest.TestCase):
    """Test cases for DeviceDetector class"""
    
    @mock.patch('os.path.exists')
    def test_specific_device(self, mock_exists):
        """Test that specified device is used if it exists"""
        mock_exists.return_value = True
        
        detector = DeviceDetector(specific_device="/dev/ttyUSB0")
        device = detector.find_device()
        
        self.assertEqual(device, "/dev/ttyUSB0")
        mock_exists.assert_called_once_with("/dev/ttyUSB0")
    
    @mock.patch('serial.tools.list_ports.comports')
    def test_find_esp_device_by_vendor(self, mock_comports):
        """Test finding ESP device by vendor ID"""
        # Set up mock ports with ESP vendor ID
        mock_comports.return_value = [
            MockPort("/dev/ttyUSB0", vid=0x10C4),  # Silicon Labs
            MockPort("/dev/ttyUSB1", vid=0x0000)   # Unknown vendor
        ]
        
        detector = DeviceDetector()
        device = detector.find_device()
        
        self.assertEqual(device, "/dev/ttyUSB0")
    
    @mock.patch('serial.tools.list_ports.comports')
    def test_find_esp_device_by_name(self, mock_comports):
        """Test finding ESP device by port name pattern"""
        # Set up mock ports without vendor ID but with recognizable names
        mock_comports.return_value = [
            MockPort("/dev/ttyUSB0"),
            MockPort("/dev/ttyACM0"),
            MockPort("/dev/ttyS0")
        ]
        
        detector = DeviceDetector()
        device = detector.find_device()
        
        # Should find ttyUSB0 or ttyACM0, but not ttyS0
        self.assertIn(device, ["/dev/ttyUSB0", "/dev/ttyACM0"])
    
    @mock.patch('os.path.exists')
    @mock.patch('serial.Serial', new=MockSerial)
    def test_is_device_connected(self, mock_exists):
        """Test device connection check"""
        mock_exists.return_value = True
        
        detector = DeviceDetector()
        detector.current_device = "/dev/ttyUSB0"
        
        # Should be connected
        self.assertTrue(detector.is_device_connected())
        
        # Test disconnected
        mock_exists.return_value = False
        self.assertFalse(detector.is_device_connected())
    
    @mock.patch('serial.tools.list_ports.comports')
    def test_list_esp_devices(self, mock_comports):
        """Test listing ESP devices"""
        # Set up mock ports
        mock_comports.return_value = [
            MockPort("/dev/ttyUSB0", "CP210x UART Bridge", vid=0x10C4),
            MockPort("/dev/ttyACM0", "ESP32 Device", vid=0x303A),
            MockPort("/dev/ttyS0", "Standard Serial Port", vid=0x0000)
        ]
        
        devices = list_esp_devices()
        
        # Should find 2 ESP devices
        self.assertEqual(len(devices), 2)
        self.assertEqual(devices[0]['device'], "/dev/ttyUSB0")
        self.assertEqual(devices[1]['device'], "/dev/ttyACM0")
        
        # Check vendor names
        self.assertEqual(devices[0]['vendor'], "Silicon Labs")
        self.assertEqual(devices[1]['vendor'], "Espressif")


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