"""Polar H10 chest strap — single-lead ECG over Bluetooth LE (~130 Hz).

Polar exposes a BLE service (PMD) that streams raw ECG. We use `bleak` (cross-
platform BLE). The H10 isn't a BrainFlow board, so this dedicated driver is the
route. Pass the device's BLE address as `address=` or let it scan by name.
"""

from __future__ import annotations

import asyncio
import threading

from ._base import DriverSource

PMD_CONTROL = "FB005C81-02E7-F387-1CAD-8ACD2D8DF0C8"
PMD_DATA = "FB005C82-02E7-F387-1CAD-8ACD2D8DF0C8"
ECG_START = bytearray([0x02, 0x00, 0x00, 0x01, 0x82, 0x00, 0x01, 0x01, 0x0E, 0x00])


class PolarH10Source(DriverSource):
    def read(self):
        import queue
        from pylsl import local_clock
        q: queue.Queue = queue.Queue(maxsize=10000)
        stop = self._stop

        def _runner():
            asyncio.run(self._stream(q, stop))

        th = threading.Thread(target=_runner, daemon=True)
        th.start()
        while not self.stopping:
            try:
                val = q.get(timeout=0.5)
            except Exception:
                continue
            yield [float(val)], local_clock()

    async def _stream(self, q, stop):
        from bleak import BleakClient, BleakScanner  # lazy
        address = self.opts.get("address")
        if not address:
            dev = await BleakScanner.find_device_by_filter(
                lambda d, ad: (d.name or "").startswith("Polar H10"))
            if not dev:
                raise RuntimeError("no Polar H10 found via BLE scan")
            address = dev.address

        async with BleakClient(address) as client:
            def _handle(_, data: bytearray):
                if data[0] != 0x00:               # 0x00 = ECG frame
                    return
                # samples: int24 LE microvolts starting at byte 10, step 3
                for i in range(10, len(data) - 2, 3):
                    v = int.from_bytes(data[i:i+3], "little", signed=True)
                    try:
                        q.put_nowait(v / 1000.0)  # uV -> mV
                    except Exception:
                        pass

            await client.start_notify(PMD_DATA, _handle)
            await client.write_gatt_char(PMD_CONTROL, ECG_START, response=True)
            while not stop.is_set():
                await asyncio.sleep(0.1)
            await client.stop_notify(PMD_DATA)
