import abc
import typing
from typing import Any, Awaitable, Generic, Mapping, Optional, TypeVar, Union
from mode.utils.typing import AsyncContextManager
from .codecs import CodecArg
from .core import HeadersArg, K, V
from .tuples import Message, MessageSentCallback, RecordMetadata

if typing.TYPE_CHECKING:
    from .app import AppT as _AppT
    from .channels import ChannelT as _ChannelT
    from .serializers import SchemaT as _SchemaT
else:
    class _AppT: ...  # noqa
    class _ChannelT: ...  # noqa
    class _SchemaT: ...   # noqa

T = TypeVar('T')


class EventT(Generic[T], AsyncContextManager):

    app: _AppT
    key: K
    value: V
    headers: Mapping
    message: Message
    acked: bool

    __slots__ = ('app', 'key', 'value', 'headers', 'message', 'acked')

    @abc.abstractmethod
    def __init__(self,
                 app: _AppT,
                 key: K,
                 value: V,
                 headers: Optional[HeadersArg],
                 message: Message) -> None:
        ...

    @abc.abstractmethod
    async def send(self,
                   channel: Union[str, _ChannelT],
                   key: K = None,
                   value: V = None,
                   partition: int = None,
                   timestamp: float = None,
                   headers: HeadersArg = None,
                   schema: _SchemaT = None,
                   key_serializer: CodecArg = None,
                   value_serializer: CodecArg = None,
                   callback: MessageSentCallback = None,
                   force: bool = False) -> Awaitable[RecordMetadata]:
        ...

    @abc.abstractmethod
    async def forward(self,
                      channel: Union[str, _ChannelT],
                      key: Any = None,
                      value: Any = None,
                      partition: int = None,
                      timestamp: float = None,
                      headers: HeadersArg = None,
                      schema: _SchemaT = None,
                      key_serializer: CodecArg = None,
                      value_serializer: CodecArg = None,
                      callback: MessageSentCallback = None,
                      force: bool = False) -> Awaitable[RecordMetadata]:
        ...

    @abc.abstractmethod
    def ack(self) -> bool:
        ...
