"""
Remote procedure call support for rxcat.

Core messages - RpcReq and RpcEvt. Each of it has identification "key", in
format "<fn_code>::<token>". Token is UUID4 string to differentiate which
exact call should receive the result. Token is generated by client.

Rpc works only in network mode (client-server communication).
"""
from typing import Any, Protocol, TypeVar

from pydantic import BaseModel
from pykit.res import Res

from rxcat._msg import Mbody


class EmptyRpcArgs(BaseModel):
    """
    When you need to use empty args for your rpc function.
    """

class SrpcSend(BaseModel):
    key: str
    body: dict
    """
    Any parseable kwargs passed to rpc fn.
    """

    @staticmethod
    def code() -> str:
        return "rxcat::srpc_send"

class SrpcRecv(BaseModel):
    key: str
    val: Any
    """
    Returned value can be anything serializable or an exception, which will
    be serialized to ErrDto.
    """

    @staticmethod
    def code() -> str:
        return "rxcat::srpc_recv"

class RpcFn(Protocol):
    """
    Function that can be used as RPC endpoint.

    Must accept data in form of validated pydantic model. Must return
    serializable object.
    """
    # it's not "data: BaseModel" since we haven't yet found how to allow it
    # to accept any instance of BaseModel
    #
    # ...and we don't want to use generics here, for now
    async def __call__(self, body: Mbody) -> Res[Any]: ...
TRpcFn = TypeVar("TRpcFn", bound=RpcFn)
