"""Hardware service for plugin hardware access.

Provides controlled access to hardware interfaces (I2C, SPI, GPIO, Serial)
with permission checking and logging.
"""
import logging
from typing import Optional, Any

logger = logging.getLogger(__name__)


class HardwareService:
    """Service for controlled hardware access.

    Provides safe wrappers for hardware interfaces that:
    - Log all access attempts
    - Handle import errors gracefully (for non-Pi systems)
    - Return None if hardware is unavailable

    Permission checking is done by PluginBase before calling these methods.
    """

    # Track which plugins have accessed which hardware
    _access_log: dict[str, list[str]] = {}

    @classmethod
    def _log_access(cls, plugin_id: str, hardware_type: str) -> None:
        """Log hardware access for auditing.

        Args:
            plugin_id: Plugin requesting access
            hardware_type: Type of hardware (i2c, gpio, spi, serial)
        """
        if plugin_id not in cls._access_log:
            cls._access_log[plugin_id] = []
        if hardware_type not in cls._access_log[plugin_id]:
            cls._access_log[plugin_id].append(hardware_type)
            logger.info(f"Plugin '{plugin_id}' accessed {hardware_type}")

    @classmethod
    def get_access_log(cls) -> dict[str, list[str]]:
        """Get the hardware access log.

        Returns:
            Dictionary mapping plugin IDs to list of accessed hardware types
        """
        return cls._access_log.copy()

    @staticmethod
    def get_i2c_bus(plugin_id: str, bus: int = 1) -> Optional[Any]:
        """Get I2C bus for a plugin.

        Args:
            plugin_id: Plugin requesting access (for logging)
            bus: I2C bus number (default: 1 for Raspberry Pi)

        Returns:
            SMBus instance or None if unavailable
        """
        HardwareService._log_access(plugin_id, "i2c")

        try:
            from smbus2 import SMBus
            return SMBus(bus)
        except ImportError:
            logger.warning("smbus2 not available - I2C access disabled")
            return None
        except Exception as e:
            logger.error(f"Failed to open I2C bus {bus}: {e}")
            return None

    @staticmethod
    def get_gpio(plugin_id: str) -> Optional[Any]:
        """Get GPIO access for a plugin.

        Returns the RPi.GPIO module if available.
        Plugin is responsible for setup/cleanup.

        Args:
            plugin_id: Plugin requesting access (for logging)

        Returns:
            GPIO module or None if unavailable
        """
        HardwareService._log_access(plugin_id, "gpio")

        try:
            import RPi.GPIO as GPIO
            return GPIO
        except ImportError:
            # Try gpiozero as fallback
            try:
                import gpiozero
                logger.info("Using gpiozero instead of RPi.GPIO")
                return gpiozero
            except ImportError:
                logger.warning("No GPIO library available (RPi.GPIO or gpiozero)")
                return None
        except Exception as e:
            logger.error(f"Failed to access GPIO: {e}")
            return None

    @staticmethod
    def get_spi(plugin_id: str, bus: int = 0, device: int = 0) -> Optional[Any]:
        """Get SPI device for a plugin.

        Args:
            plugin_id: Plugin requesting access (for logging)
            bus: SPI bus number (default: 0)
            device: SPI device/chip select (default: 0)

        Returns:
            SpiDev instance or None if unavailable
        """
        HardwareService._log_access(plugin_id, "spi")

        try:
            import spidev
            spi = spidev.SpiDev()
            spi.open(bus, device)
            return spi
        except ImportError:
            logger.warning("spidev not available - SPI access disabled")
            return None
        except Exception as e:
            logger.error(f"Failed to open SPI bus {bus} device {device}: {e}")
            return None

    @staticmethod
    def get_serial(
        plugin_id: str,
        port: str,
        baudrate: int = 9600,
        timeout: float = 1.0
    ) -> Optional[Any]:
        """Get serial port for a plugin.

        Args:
            plugin_id: Plugin requesting access (for logging)
            port: Serial port path (e.g., '/dev/ttyUSB0')
            baudrate: Baud rate (default: 9600)
            timeout: Read timeout in seconds (default: 1.0)

        Returns:
            Serial instance or None if unavailable
        """
        HardwareService._log_access(plugin_id, "serial")

        try:
            import serial
            return serial.Serial(port, baudrate, timeout=timeout)
        except ImportError:
            logger.warning("pyserial not available - serial access disabled")
            return None
        except Exception as e:
            logger.error(f"Failed to open serial port {port}: {e}")
            return None

    @staticmethod
    def is_hardware_available(hardware_type: str) -> bool:
        """Check if a hardware type is available on this system.

        Args:
            hardware_type: One of 'i2c', 'gpio', 'spi', 'serial'

        Returns:
            True if the hardware library is importable
        """
        try:
            if hardware_type == "i2c":
                from smbus2 import SMBus
                return True
            elif hardware_type == "gpio":
                try:
                    import RPi.GPIO
                    return True
                except ImportError:
                    import gpiozero
                    return True
            elif hardware_type == "spi":
                import spidev
                return True
            elif hardware_type == "serial":
                import serial
                return True
            else:
                return False
        except ImportError:
            return False

    @staticmethod
    def get_available_hardware() -> list[str]:
        """Get list of available hardware types.

        Returns:
            List of available hardware type names
        """
        available = []
        for hw_type in ["i2c", "gpio", "spi", "serial"]:
            if HardwareService.is_hardware_available(hw_type):
                available.append(hw_type)
        return available
