"""SteelSeries GameSense LED client — controls SteelSeries RGB peripherals via REST API."""

import json
import os
import platform
from datetime import datetime, timezone
from typing import List, Optional, Tuple, Union

import numpy as np

from wled_controller.core.devices.led_client import DeviceHealth, LEDClient
from wled_controller.utils import get_logger

logger = get_logger(__name__)

# Game registration constants
GAME_NAME = "WLED_SCREEN_CTRL"
GAME_DISPLAY_NAME = "WLED Screen Controller"
EVENT_NAME = "PIXEL_DATA"


def _get_gamesense_address() -> Optional[str]:
    """Discover the SteelSeries GameSense address from coreProps.json."""
    if platform.system() == "Windows":
        props_path = os.path.join(
            os.environ.get("PROGRAMDATA", r"C:\ProgramData"),
            "SteelSeries", "SteelSeries Engine 3", "coreProps.json",
        )
    elif platform.system() == "Darwin":
        props_path = os.path.expanduser(
            "~/Library/Application Support/SteelSeries Engine 3/coreProps.json"
        )
    else:
        # Linux — SteelSeries Engine not officially supported
        props_path = os.path.expanduser(
            "~/.config/SteelSeries Engine 3/coreProps.json"
        )

    try:
        with open(props_path, "r") as f:
            data = json.load(f)
        return data.get("address")
    except (FileNotFoundError, json.JSONDecodeError, KeyError):
        return None


# GameSense device-zone mapping
GAMESENSE_ZONES = {
    "keyboard": ("rgb-per-key-zones", "all"),
    "mouse": ("mouse", "wheel"),
    "headset": ("headset", "earcups"),
    "mousepad": ("mousepad", "all"),
    "indicator": ("indicator", "one"),
}


class GameSenseClient(LEDClient):
    """LED client that controls SteelSeries peripherals via GameSense REST API.

    GameSense uses a register-bind-send pattern:
      1. Register game + event
      2. Bind event to device zone with context-color handler
      3. Send color events with RGB data in frame payload

    The API address is discovered from coreProps.json at runtime.
    """

    def __init__(
        self,
        url: str = "",
        led_count: int = 0,
        gamesense_device_type: str = "keyboard",
        **kwargs,
    ):
        self._address = url.replace("gamesense://", "").strip() if url else ""
        self._led_count = led_count
        self._gs_device_type = gamesense_device_type
        self._connected = False
        self._http_client = None
        self._base_url: Optional[str] = None

    async def connect(self) -> bool:
        import httpx

        # Discover GameSense address
        address = self._address or _get_gamesense_address()
        if not address:
            raise RuntimeError(
                "Cannot find SteelSeries Engine. "
                "Ensure SteelSeries GG/Engine is running."
            )

        self._base_url = f"http://{address}"
        self._http_client = httpx.AsyncClient(timeout=5.0)

        try:
            # Register game
            await self._http_client.post(
                f"{self._base_url}/game_metadata",
                json={
                    "game": GAME_NAME,
                    "game_display_name": GAME_DISPLAY_NAME,
                    "developer": "WLED-SC",
                },
            )

            # Register event
            await self._http_client.post(
                f"{self._base_url}/register_game_event",
                json={
                    "game": GAME_NAME,
                    "event": EVENT_NAME,
                    "min_value": 0,
                    "max_value": 100,
                    "value_optional": True,
                },
            )

            # Bind event to device zone with context-color mode
            zone_info = GAMESENSE_ZONES.get(
                self._gs_device_type, ("rgb-per-key-zones", "all")
            )
            device_type, zone = zone_info

            await self._http_client.post(
                f"{self._base_url}/bind_game_event",
                json={
                    "game": GAME_NAME,
                    "event": EVENT_NAME,
                    "handlers": [
                        {
                            "device-type": device_type,
                            "zone": zone,
                            "mode": "context-color",
                            "context-frame-key": "zone-color",
                        }
                    ],
                },
            )
        except Exception as e:
            logger.error("GameSense setup failed: %s", e)
            await self._http_client.aclose()
            self._http_client = None
            raise

        self._connected = True
        logger.info(
            "GameSense client connected: device=%s address=%s leds=%d",
            self._gs_device_type, address, self._led_count,
        )
        return True

    async def close(self) -> None:
        if self._http_client and self._base_url:
            try:
                # Remove game registration
                await self._http_client.post(
                    f"{self._base_url}/remove_game",
                    json={"game": GAME_NAME},
                )
            except Exception:
                pass

        if self._http_client:
            await self._http_client.aclose()
            self._http_client = None

        self._connected = False
        self._base_url = None
        logger.info("GameSense client closed")

    @property
    def is_connected(self) -> bool:
        return self._connected and self._http_client is not None

    async def send_pixels(
        self,
        pixels: Union[List[Tuple[int, int, int]], np.ndarray],
        brightness: int = 255,
    ) -> bool:
        if not self.is_connected or not self._http_client:
            return False

        if isinstance(pixels, np.ndarray):
            pixel_arr = pixels
        else:
            pixel_arr = np.array(pixels, dtype=np.uint8)

        # Note: brightness already applied by processor loop (_cached_brightness)

        # Use average color for single-zone devices, or first N for multi-zone
        if len(pixel_arr) == 0:
            return True

        # Compute average color for the zone
        avg = pixel_arr.mean(axis=0)
        r = int(avg[0])
        g = int(avg[1])
        b = int(avg[2])

        event_data = {
            "game": GAME_NAME,
            "event": EVENT_NAME,
            "data": {
                "value": 100,
                "frame": {
                    "zone-color": {
                        "red": r,
                        "green": g,
                        "blue": b,
                    }
                },
            },
        }

        try:
            resp = await self._http_client.post(
                f"{self._base_url}/game_event",
                json=event_data,
            )
            return resp.status_code == 200
        except Exception as e:
            logger.error("GameSense send failed: %s", e)
            return False

    @classmethod
    async def check_health(
        cls,
        url: str,
        http_client,
        prev_health: Optional[DeviceHealth] = None,
    ) -> DeviceHealth:
        """Check if SteelSeries Engine is running."""
        address = url.replace("gamesense://", "").strip() if url else None
        if not address:
            address = _get_gamesense_address()

        if not address:
            return DeviceHealth(
                online=False,
                error="SteelSeries Engine not found (coreProps.json missing)",
                last_checked=datetime.now(timezone.utc),
            )

        try:
            resp = await http_client.get(f"http://{address}", timeout=3.0)
            if resp.status_code < 500:
                return DeviceHealth(
                    online=True,
                    latency_ms=0.0,
                    device_name="SteelSeries GameSense",
                    last_checked=datetime.now(timezone.utc),
                )
        except Exception:
            pass

        return DeviceHealth(
            online=False,
            error="SteelSeries Engine not responding",
            last_checked=datetime.now(timezone.utc),
        )
