"""PiSugar UPS driver.

Communicates with pisugar-server daemon via TCP socket.
Supports PiSugar 2, PiSugar 2 Pro, PiSugar 3, and PiSugar 3 Plus.
"""
import asyncio
import socket
from typing import Optional, Tuple
from .base import UPSDriver, UPSData


class PiSugarDriver(UPSDriver):
    """Driver for PiSugar UPS devices."""

    # Known I2C addresses for PiSugar devices
    PISUGAR_I2C_ADDRESSES = [0x57, 0x32]  # Battery IC, RTC

    @classmethod
    async def detect(cls, host: str = "127.0.0.1", port: int = 8423) -> Tuple[bool, Optional[str]]:
        """Detect if PiSugar hardware is available.

        Tries multiple detection methods:
        1. Check if pisugar-server daemon is responding
        2. Scan I2C bus for known PiSugar addresses (fallback)

        Args:
            host: PiSugar server host
            port: PiSugar server port

        Returns:
            Tuple of (detected: bool, model_name: Optional[str])
        """
        # Method 1: Try to connect to pisugar-server daemon
        try:
            reader, writer = await asyncio.wait_for(
                asyncio.open_connection(host, port),
                timeout=2.0
            )

            try:
                # Send model query
                writer.write(b"get model\n")
                await writer.drain()

                response = await asyncio.wait_for(
                    reader.readline(),
                    timeout=2.0
                )

                model = response.decode().strip()
                if model and "model:" in model.lower():
                    # Parse "model: PiSugar 3" format
                    parts = model.split(":", 1)
                    if len(parts) > 1:
                        model_name = parts[1].strip()
                        if model_name and model_name.lower() != "unknown":
                            return (True, model_name)

            finally:
                writer.close()
                await writer.wait_closed()

        except (asyncio.TimeoutError, ConnectionRefusedError, OSError):
            pass

        # Method 2: Check I2C addresses (requires smbus2)
        # NOTE: We only log if hardware is found - PiSugarDriver requires the daemon
        # If daemon isn't running, we can't use PiSugarDriver even if hardware exists
        i2c_found = False
        try:
            from smbus2 import SMBus
            bus = SMBus(1)
            try:
                for addr in cls.PISUGAR_I2C_ADDRESSES:
                    try:
                        bus.read_byte(addr)
                        i2c_found = True
                        break
                    except OSError:
                        continue
            finally:
                bus.close()
        except ImportError:
            pass
        except Exception:
            pass

        if i2c_found:
            # Hardware found but daemon not running - can't use PiSugarDriver
            print("PiSugar hardware detected via I2C but pisugar-server daemon not running")
            print("Install pisugar-server or start the daemon to enable PiSugar UPS support")

        return (False, None)

    def __init__(self, host: str = "127.0.0.1", port: int = 8423, timeout: float = 5.0):
        """Initialize PiSugar driver.

        Args:
            host: PiSugar server host (default: localhost)
            port: PiSugar server port (default: 8423)
            timeout: Command timeout in seconds (default: 5.0)
        """
        self._host = host
        self._port = port
        self._timeout = timeout
        self._model: Optional[str] = None
        self._available = False

    async def initialize(self) -> bool:
        """Initialize connection to PiSugar daemon.

        Returns:
            True if initialization successful, False otherwise
        """
        try:
            # Test connection by getting model
            self._model = await self._send_command("get model")
            if self._model and self._model != "Unknown":
                self._available = True
                return True
            else:
                self._available = False
                return False

        except Exception as e:
            print(f"Failed to initialize PiSugar: {e}")
            self._available = False
            return False

    async def read_data(self) -> UPSData:
        """Read current battery data from PiSugar.

        Returns:
            UPSData object with current readings
        """
        if not self._available:
            raise RuntimeError("PiSugar not initialized or not available")

        # Execute commands in parallel for efficiency
        results = await asyncio.gather(
            self._send_command("get battery"),
            self._send_command("get battery_v"),
            self._send_command("get battery_i"),
            self._send_command("get battery_power_plugged"),
            return_exceptions=True
        )

        # Parse results
        percentage = self._parse_float(results[0], 0.0)
        voltage = self._parse_float(results[1], 0.0)
        current = self._parse_float(results[2], 0.0)
        is_charging = self._parse_bool(results[3], False)

        return UPSData(
            percentage=percentage,
            voltage=voltage,
            current=current,
            is_charging=is_charging,
            temperature=None,  # PiSugar doesn't provide temperature
            time_remaining=None  # Would need battery capacity config to calculate
        )

    async def is_available(self) -> bool:
        """Check if PiSugar hardware is available.

        Returns:
            True if hardware is accessible, False otherwise
        """
        if not self._available:
            # Try to reinitialize
            return await self.initialize()
        return True

    def close(self):
        """Close connection to PiSugar (stateless, nothing to close)."""
        self._available = False

    def get_model_name(self) -> str:
        """Get the PiSugar model name.

        Returns:
            Human-readable model name
        """
        return self._model or "PiSugar"

    async def _send_command(self, command: str) -> str:
        """Send command to PiSugar server and get response.

        Args:
            command: Command to send (e.g., "get battery")

        Returns:
            Response string from server

        Raises:
            RuntimeError: If connection fails or times out
        """
        try:
            # Connect to PiSugar server
            reader, writer = await asyncio.wait_for(
                asyncio.open_connection(self._host, self._port),
                timeout=self._timeout
            )

            try:
                # Send command
                writer.write(f"{command}\n".encode())
                await writer.drain()

                # Read response (single line)
                response = await asyncio.wait_for(
                    reader.readline(),
                    timeout=self._timeout
                )

                return response.decode().strip()

            finally:
                writer.close()
                await writer.wait_closed()

        except asyncio.TimeoutError:
            raise RuntimeError(f"PiSugar command timeout: {command}")
        except ConnectionRefusedError:
            raise RuntimeError("PiSugar server not running (connection refused)")
        except Exception as e:
            raise RuntimeError(f"PiSugar communication error: {e}")

    def _parse_float(self, value, default: float = 0.0) -> float:
        """Parse float value from response.

        Args:
            value: Response value (could be string, float, or Exception)
            default: Default value if parsing fails

        Returns:
            Parsed float value or default
        """
        if isinstance(value, Exception):
            return default

        try:
            # PiSugar responses are often in format "battery: 85.5"
            if isinstance(value, str):
                # Try to extract number from response
                parts = value.split(":")
                if len(parts) > 1:
                    return float(parts[1].strip())
                else:
                    return float(value.strip())
            return float(value)
        except (ValueError, AttributeError):
            return default

    def _parse_bool(self, value, default: bool = False) -> bool:
        """Parse boolean value from response.

        Args:
            value: Response value (could be string, bool, or Exception)
            default: Default value if parsing fails

        Returns:
            Parsed boolean value or default
        """
        if isinstance(value, Exception):
            return default

        try:
            if isinstance(value, bool):
                return value

            if isinstance(value, str):
                # PiSugar returns "battery_power_plugged: true" or "false"
                parts = value.split(":")
                if len(parts) > 1:
                    return parts[1].strip().lower() == "true"
                else:
                    return value.strip().lower() in ("true", "1", "yes")

            return bool(value)
        except (ValueError, AttributeError):
            return default
