"""
Active ping
"""

from __future__ import annotations

from moat.lib.micro import sleep, wait_for
from moat.lib.rpc import BaseCmd

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from moat.lib.rpc import Msg


class _NotGiven:
    pass


class Cmd(BaseCmd):
    """
    An app to do active ping-ing: keepalive, discover dead links.

    Config:
        t: timeout
        d: delay between pings
        p: Path to call.
        s: flag whether to do streaming.
    """

    doc = dict(
        _d="echo",
        _i="Any:data",
        _o="Any:data (echoed)",
        _s=[],
        _c=dict(
            _d="echo service",
            p="path:dest for active echo-ing",
            t="int:error timeout (3 s)",
            d="int:delay(59 s)",
            s="bool:streamed?",
        ),
    )

    async def stream(self, msg: Msg):
        """
        Keepalive/liveness monitor. Echoes incoming data.
        """
        if msg.can_stream:
            async with msg.stream(*msg.args, **msg.kw) as md:
                async for m in md:
                    await msg.send(*m.args, **m.kw)
        await msg.result(*msg.args, **msg.kw)

    async def task(self):
        """
        Sender task. Counts and sends.
        """
        if (p := self.cfg.get("p", None)) is None:
            return await super().task()
        root = self.root
        if root is None:
            raise RuntimeError("Not attached")
        async with root.sub_at(p) as sub:
            if self.cfg.get("s", False):
                async with sub.stream() as msg:
                    self.set_ready()
                    await self._do(msg.send)
            else:
                self.set_ready()
                await self._do(sub)

    async def _do(self, sender):
        n = 0
        while True:
            res = await wait_for(self.cfg.get("t", 3), sender, n)
            if res != n:
                raise RuntimeError("wrong ping echo: want {n}, got {r !r}")
            n = (n + 1) if n < 9 else 1
            await sleep(self.cfg.get("d", 59))
