import asyncio

from microdot import Microdot
from microdot.websocket import WebSocketError, with_websocket

import aioespnow
import machine
import network
from settings import Settings
from espnow_wire import BROADCAST_MAC, pack_ws_uplink

wdt = machine.WDT(timeout=10000)
wdt.feed()
settings = Settings()
print(settings)

app = Microdot()

ch = settings.get("wifi_channel", 6)
try:
    ch = max(1, min(11, int(ch)))
except (TypeError, ValueError):
    ch = 6

ap_if = network.WLAN(network.AP_IF)
ap_if.active(True)
ap_if.config(
    ssid=settings.get("name"),
    password=settings.get("ap_password"),
    channel=ch,
)
print(ap_if.ifconfig())

sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
print(sta_if.config("channel"))

esp = aioespnow.AIOESPNow()
esp.active(True)
esp.add_peer(BROADCAST_MAC)

clients = set()


@app.route("/ws")
@with_websocket
async def ws(request, ws):
    clients.add(ws)
    try:
        while True:
            try:
                raw = await ws.receive()
            except WebSocketError as err:
                print(err)
                break
            if not raw:
                break
            try:
                await esp.asend(BROADCAST_MAC, raw)
            except Exception as err:
                print(err)
                break
    finally:
        clients.discard(ws)


async def _espnow_receive_loop():
    async for host, msg in esp:
        if not host or not msg:
            continue
        print("espnow rx", len(msg), "B")
        frame = pack_ws_uplink(host, msg)
        dead = []
        for client in list(clients):
            try:
                await client.send(frame)
            except Exception:
                dead.append(client)
        for client in dead:
            clients.discard(client)


async def _wdt_feed_loop():
    while True:
        await asyncio.sleep(1)
        wdt.feed()


async def main():
    asyncio.create_task(_wdt_feed_loop())
    asyncio.create_task(_espnow_receive_loop())
    await app.start_server(host="0.0.0.0", port=80)


if __name__ == "__main__":
    asyncio.run(main())
