import typing as t

from skoll.result import Result
from skoll.config import SMTPConfig

from .primitives import ID
from .objects import Entity, IPInfo
from .messaging import Message, RawMessage, Service
from .typing import Criteria, ListPage, AuthzWriteChange, AuthzPrecondition, AuthzLookupResult


__all__ = ["DB", "Repository", "Mediator", "Authz", "EmailSender", "IPInfoProvider"]


class IPInfoProvider(t.Protocol):

    async def __call__(self, ip: str) -> IPInfo | None: ...


class DB[T = t.Any](t.Protocol):

    async def close(self) -> None: ...
    async def connect(self) -> None: ...
    def session(self) -> t.AsyncContextManager[T]: ...
    def transaction(self) -> t.AsyncContextManager[T]: ...


class Repository[T: Entity](t.Protocol):

    async def save(self, state: T) -> None: ...
    async def delete(self, criteria: Criteria) -> None: ...
    async def exist(self, criteria: Criteria) -> bool: ...
    async def get(self, criteria: Criteria) -> T | None: ...
    async def list(self, criteria: Criteria) -> ListPage[T]: ...


class Mediator(t.Protocol):

    async def connect(self) -> None: ...
    async def disconnect(self) -> None: ...
    async def unsubscribe(self, id: ID) -> None: ...

    async def subscribe(self, service: Service) -> ID: ...
    async def publish(self, *msg: Message | RawMessage) -> None: ...
    async def request(self, msg: Message | RawMessage) -> Result[t.Any]: ...


class Authz(t.Protocol):

    async def check(self, tuple: str, cxt: dict[str, t.Any] | None = None) -> None: ...
    async def write(
        self, changes: list[AuthzWriteChange], preconditions: list[AuthzPrecondition] | None = None
    ) -> str: ...
    async def lookup(
        self, filter: str, cxt: dict[str, t.Any] | None = None, limit: int | None = None, cursor: str | None = None
    ) -> AuthzLookupResult: ...


class EmailSender(t.Protocol):

    async def __call__(self, to: str, title: str, html_body: str, config: SMTPConfig = SMTPConfig.DEFAULT) -> None: ...
