"""ADS1115 service layer with support for real hardware and mock mode for testing."""

import os
import random
from typing import Optional, Dict, Any
from dataclasses import dataclass


@dataclass
class ADS1115Config:
    """Configuration for ADS1115 device."""
    i2c_address: int = 0x48
    gain: float = 1.0
    data_rate: int = 128
    mode: str = "single"  # single or continuous


class MockADS1115:
    """Mock ADS1115 for testing in Docker/non-RPi environments."""
    
    def __init__(self, config: ADS1115Config):
        self.config = config
        self._last_pin = None
        
    def read(self, pin: int, is_differential: bool = False) -> int:
        """Simulate ADC reading with realistic noise."""
        base_value = 5000 + pin * 1000  # Different base for each pin
        noise = random.randint(-100, 100)
        return base_value + noise
    
    def get_voltage(self, pin: int) -> float:
        """Convert raw reading to voltage."""
        raw = self.read(pin)
        # For gain=1, 16-bit ADC: ±4.096V range
        max_value = 32767
        voltage_range = 4.096 / self.config.gain
        return (raw / max_value) * voltage_range


class ADS1115Service:
    """Service for ADS1115 operations with automatic mock fallback."""
    
    def __init__(self, config: Optional[ADS1115Config] = None):
        self.config = config or ADS1115Config()
        self._adc = None
        self._mock_mode = False
        
    def initialize(self) -> bool:
        """Initialize ADS1115 device or fall back to mock."""
        use_mock = os.getenv('ADS1115_MOCK', 'false').lower() == 'true'
        
        if use_mock:
            self._adc = MockADS1115(self.config)
            self._mock_mode = True
            return True
            
        try:
            import board
            import busio
            from adafruit_ads1x15.ads1115 import ADS1115 as RealADS1115
            from adafruit_ads1x15.analog_in import AnalogIn
            
            i2c = busio.I2C(board.SCL, board.SDA)
            self._adc = RealADS1115(
                i2c,
                gain=self.config.gain,
                data_rate=self.config.data_rate,
                address=self.config.i2c_address
            )
            self._mock_mode = False
            return True
        except (ImportError, Exception) as e:
            # Fall back to mock mode
            self._adc = MockADS1115(self.config)
            self._mock_mode = True
            return True
    
    @property
    def is_mock(self) -> bool:
        return self._mock_mode
    
    def read_channel(self, channel: int) -> Dict[str, Any]:
        """Read a single ADC channel."""
        if self._adc is None:
            raise RuntimeError("ADC not initialized")
            
        raw_value = self._adc.read(channel)
        voltage = self._adc.get_voltage(channel) if hasattr(self._adc, 'get_voltage') else None
        
        if voltage is None:
            # Calculate voltage manually
            max_value = 32767
            voltage_range = 4.096 / self.config.gain
            voltage = (raw_value / max_value) * voltage_range
        
        return {
            "channel": channel,
            "raw_value": raw_value,
            "voltage": round(voltage, 6),
            "gain": self.config.gain,
            "data_rate": self.config.data_rate
        }
    
    def read_all_channels(self) -> Dict[str, Any]:
        """Read all 4 ADC channels."""
        results = []
        for i in range(4):
            results.append(self.read_channel(i))
        return {
            "channels": results,
            "mock_mode": self._mock_mode,
            "i2c_address": hex(self.config.i2c_address)
        }
    
    def get_config(self) -> Dict[str, Any]:
        """Get current device configuration."""
        return {
            "i2c_address": hex(self.config.i2c_address),
            "gain": self.config.gain,
            "data_rate": self.config.data_rate,
            "mode": self.config.mode,
            "mock_mode": self._mock_mode,
            "available_gains": [2/3, 1, 2, 4, 8, 16],
            "available_data_rates": [8, 16, 32, 64, 128, 250, 475, 860]
        }
    
    def update_config(self, **kwargs) -> Dict[str, Any]:
        """Update device configuration."""
        if 'gain' in kwargs:
            self.config.gain = float(kwargs['gain'])
        if 'data_rate' in kwargs:
            self.config.data_rate = int(kwargs['data_rate'])
        if 'i2c_address' in kwargs:
            self.config.i2c_address = int(kwargs['i2c_address'], 16) if isinstance(kwargs['i2c_address'], str) else int(kwargs['i2c_address'])
        if 'mode' in kwargs:
            self.config.mode = kwargs['mode']
        
        # Reinitialize with new config
        self.initialize()
        return self.get_config()


# Global service instance
_service_instance: Optional[ADS1115Service] = None


def get_service() -> ADS1115Service:
    """Get or create the global service instance."""
    global _service_instance
    if _service_instance is None:
        _service_instance = ADS1115Service()
        _service_instance.initialize()
    return _service_instance


def reset_service():
    """Reset the global service instance."""
    global _service_instance
    _service_instance = None
