"""
This type stub file was generated by pyright.
"""

import asyncio
import ssl
import nats.js
from dataclasses import dataclass
from typing import Any, Awaitable, Callable, Dict, List, Optional, Tuple, Union
from urllib.parse import ParseResult
from .msg import Msg
from .subscription import Subscription

__version__ = ...
__lang__ = ...
_logger = ...
PROTOCOL = ...
INFO_OP = ...
CONNECT_OP = ...
PING_OP = ...
PONG_OP = ...
OK_OP = ...
ERR_OP = ...
_CRLF_ = ...
_CRLF_LEN_ = ...
_SPC_ = ...
_SPC_BYTE_ = ...
EMPTY = ...
PING_PROTO = ...
PONG_PROTO = ...
DEFAULT_INBOX_PREFIX = ...
DEFAULT_PENDING_SIZE = ...
DEFAULT_BUFFER_SIZE = ...
DEFAULT_RECONNECT_TIME_WAIT = ...
DEFAULT_MAX_RECONNECT_ATTEMPTS = ...
DEFAULT_PING_INTERVAL = ...
DEFAULT_MAX_OUTSTANDING_PINGS = ...
DEFAULT_MAX_PAYLOAD_SIZE = ...
DEFAULT_MAX_FLUSHER_QUEUE_SIZE = ...
DEFAULT_FLUSH_TIMEOUT = ...
DEFAULT_CONNECT_TIMEOUT = ...
DEFAULT_DRAIN_TIMEOUT = ...
MAX_CONTROL_LINE_SIZE = ...
NATS_HDR_LINE = ...
NATS_HDR_LINE_SIZE = ...
NO_RESPONDERS_STATUS = ...
CTRL_STATUS = ...
STATUS_MSG_LEN = ...
Callback = Callable[[], Awaitable[None]]
ErrorCallback = Callable[[Exception], Awaitable[None]]
JWTCallback = Callable[[], Union[bytearray, bytes]]
Credentials = Union[str, Tuple[str, str]]
SignatureCallback = Callable[[str], bytes]
@dataclass
class Srv:
    """
    Srv is a helper data structure to hold state of a server.
    """
    uri: ParseResult
    reconnects: int = ...
    last_attempt: Optional[float] = ...
    did_connect: bool = ...
    discovered: bool = ...
    tls_name: Optional[str] = ...
    server_version: Optional[str] = ...


class ServerVersion:
    def __init__(self, server_version: str) -> None:
        ...
    
    def parse_version(self) -> None:
        ...
    
    @property
    def major(self) -> int:
        ...
    
    @property
    def minor(self) -> int:
        ...
    
    @property
    def patch(self) -> int:
        ...
    
    @property
    def dev(self) -> str:
        ...
    
    def __repr__(self) -> str:
        ...
    


class Client:
    """
    Asyncio based client for NATS.
    """
    msg_class: type[Msg] = ...
    DISCONNECTED = ...
    CONNECTED = ...
    CLOSED = ...
    RECONNECTING = ...
    CONNECTING = ...
    DRAINING_SUBS = ...
    DRAINING_PUBS = ...
    def __repr__(self) -> str:
        ...
    
    def __init__(self) -> None:
        ...
    
    async def connect(self, servers: Union[str, List[str]] = ..., error_cb: Optional[ErrorCallback] = ..., disconnected_cb: Optional[Callback] = ..., closed_cb: Optional[Callback] = ..., discovered_server_cb: Optional[Callback] = ..., reconnected_cb: Optional[Callback] = ..., name: Optional[str] = ..., pedantic: bool = ..., verbose: bool = ..., allow_reconnect: bool = ..., connect_timeout: int = ..., reconnect_time_wait: int = ..., max_reconnect_attempts: int = ..., ping_interval: int = ..., max_outstanding_pings: int = ..., dont_randomize: bool = ..., flusher_queue_size: int = ..., no_echo: bool = ..., tls: Optional[ssl.SSLContext] = ..., tls_hostname: Optional[str] = ..., tls_handshake_first: bool = ..., user: Optional[str] = ..., password: Optional[str] = ..., token: Optional[str] = ..., drain_timeout: int = ..., signature_cb: Optional[SignatureCallback] = ..., user_jwt_cb: Optional[JWTCallback] = ..., user_credentials: Optional[Credentials] = ..., nkeys_seed: Optional[str] = ..., nkeys_seed_str: Optional[str] = ..., inbox_prefix: Union[str, bytes] = ..., pending_size: int = ..., flush_timeout: Optional[float] = ...) -> None:
        """
        Establishes a connection to NATS.

        :param servers: NATS Connection
        :param name: Label the connection with name (shown in NATS monitoring)
        :param error_cb: Callback to report errors.
        :param disconnected_cb: Callback to report disconnection from NATS.
        :param closed_cb: Callback to report when client stops reconnection to NATS.
        :param discovered_server_cb: Callback to report when a new server joins the cluster.
        :param pending_size: Max size of the pending buffer for publishing commands.
        :param flush_timeout: Max duration to wait for a forced flush to occur.

        Connecting setting all callbacks::

            import asyncio
            import nats

            async def main():
                async def disconnected_cb():
                    print('Got disconnected!')

                async def reconnected_cb():
                    print(f'Got reconnected to {nc.connected_url.netloc}')

                async def error_cb(e):
                    print(f'There was an error: {e}')

                async def closed_cb():
                    print('Connection is closed')

                # Connect to NATS with logging callbacks.
                nc = await nats.connect('demo.nats.io',
                                         error_cb=error_cb,
                                         reconnected_cb=reconnected_cb,
                                         disconnected_cb=disconnected_cb,
                                         closed_cb=closed_cb,
                                         )

                async def handler(msg):
                    print(f'Received a message on {msg.subject} {msg.reply}: {msg.data}')
                    await msg.respond(b'OK')

                sub = await nc.subscribe('help.please', cb=handler)

                resp = await nc.request('help.please', b'help')
                print('Response:', resp)

                await nc.close()

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

        Using a context manager::

            import asyncio
            import nats

            async def main():

                is_done = asyncio.Future()

                async def closed_cb():
                    print('Connection to NATS is closed.')
                    is_done.set_result(True)

                async with (await nats.connect('nats://demo.nats.io:4222', closed_cb=closed_cb)) as nc:
                    print(f'Connected to NATS at {nc.connected_url.netloc}...')

                    async def subscribe_handler(msg):
                        subject = msg.subject
                        reply = msg.reply
                        data = msg.data.decode()
                        print('Received a message on '{subject} {reply}': {data}'.format(
                            subject=subject, reply=reply, data=data))

                    await nc.subscribe('discover', cb=subscribe_handler)
                    await nc.flush()

                    for i in range(0, 10):
                        await nc.publish('discover', b'hello world')
                        await asyncio.sleep(0.1)

                await asyncio.wait_for(is_done, 60.0)

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

        """
        ...
    
    async def close(self) -> None:
        """
        Closes the socket to which we are connected and
        sets the client to be in the CLOSED state.
        No further reconnections occur once reaching this point.
        """
        ...
    
    async def drain(self) -> None:
        """
        drain will put a connection into a drain state. All subscriptions will
        immediately be put into a drain state. Upon completion, the publishers
        will be drained and can not publish any additional messages. Upon draining
        of the publishers, the connection will be closed. Use the `closed_cb`
        option to know when the connection has moved from draining to closed.

        """
        ...
    
    async def publish(self, subject: str, payload: bytes = ..., reply: str = ..., headers: Optional[Dict[str, str]] = ...) -> None:
        """
        Publishes a NATS message.

        :param subject: Subject to which the message will be published.
        :param payload: Message data.
        :param reply: Inbox to which a responder can respond.
        :param headers: Optional message header.

        ::

            import asyncio
            import nats

            async def main():
                nc = await nats.connect('demo.nats.io')

                # Publish as message with an inbox.
                inbox = nc.new_inbox()
                sub = await nc.subscribe('hello')

                # Simple publishing
                await nc.publish('hello', b'Hello World!')

                # Publish with a reply
                await nc.publish('hello', b'Hello World!', reply=inbox)

                # Publish with headers
                await nc.publish('hello', b'With Headers', headers={'Foo':'Bar'})

                while True:
                    try:
                        msg = await sub.next_msg()
                    except:
                        break
                    print('----------------------')
                    print('Subject:', msg.subject)
                    print('Reply  :', msg.reply)
                    print('Data   :', msg.data)
                    print('Headers:', msg.header)

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

        """
        ...
    
    async def subscribe(self, subject: str, queue: str = ..., cb: Optional[Callable[[Msg], Awaitable[None]]] = ..., future: Optional[asyncio.Future[Any]] = ..., max_msgs: int = ..., pending_msgs_limit: int = ..., pending_bytes_limit: int = ...) -> Subscription:
        """
        subscribe registers interest in a given subject.

        If a callback is provided, messages will be processed asychronously.

        If a callback isn't provided, messages can be retrieved via an
        asynchronous iterator on the returned subscription object.
        """
        ...
    
    async def request(self, subject: str, payload: bytes = ..., timeout: float = ..., old_style: bool = ..., headers: Optional[Dict[str, Any]] = ...) -> Msg:
        """
        Implements the request/response pattern via pub/sub
        using a single wildcard subscription that handles
        the responses.

        """
        ...
    
    def new_inbox(self) -> str:
        """
        new_inbox returns a unique inbox that can be used
        for NATS requests or subscriptions::

           # Create unique subscription to receive direct messages.
           inbox = nc.new_inbox()
           sub = await nc.subscribe(inbox)
           nc.publish('broadcast', b'', reply=inbox)
           msg = sub.next_msg()
        """
        ...
    
    async def flush(self, timeout: int = ...) -> None:
        """
        Sends a ping to the server expecting a pong back ensuring
        what we have written so far has made it to the server and
        also enabling measuring of roundtrip time.
        In case a pong is not returned within the allowed timeout,
        then it will raise nats.errors.TimeoutError
        """
        ...
    
    @property
    def connected_url(self) -> Optional[ParseResult]:
        ...
    
    @property
    def servers(self) -> List[ParseResult]:
        ...
    
    @property
    def discovered_servers(self) -> List[ParseResult]:
        ...
    
    @property
    def max_payload(self) -> int:
        """
        Returns the max payload which we received from the servers INFO
        """
        ...
    
    @property
    def client_id(self) -> Optional[int]:
        """
        Returns the client id which we received from the servers INFO
        """
        ...
    
    @property
    def last_error(self) -> Optional[Exception]:
        """
        Returns the last error which may have occurred.
        """
        ...
    
    @property
    def pending_data_size(self) -> int:
        ...
    
    @property
    def is_closed(self) -> bool:
        ...
    
    @property
    def is_reconnecting(self) -> bool:
        ...
    
    @property
    def is_connected(self) -> bool:
        ...
    
    @property
    def is_connecting(self) -> bool:
        ...
    
    @property
    def is_draining(self) -> bool:
        ...
    
    @property
    def is_draining_pubs(self) -> bool:
        ...
    
    @property
    def connected_server_version(self) -> ServerVersion:
        """
        Returns the ServerVersion of the server to which the client
        is currently connected.
        """
        ...
    
    @property
    def ssl_context(self) -> ssl.SSLContext:
        ...
    
    async def __aenter__(self) -> Client:
        """For when NATS client is used in a context manager"""
        ...
    
    async def __aexit__(self, *exc_info) -> None:
        """Close connection to NATS when used in a context manager"""
        ...
    
    def jetstream(self, **opts: dict[Any, Any]) -> nats.js.JetStreamContext:
        """
        jetstream returns a context that can be used to produce and consume
        messages from NATS JetStream.

        :param prefix: Default JetStream API Prefix.
        :param domain: Optional domain used by the JetStream API.
        :param timeout: Timeout for all JS API actions.

        ::

            import asyncio
            import nats

            async def main():
                nc = await nats.connect()
                js = nc.jetstream()

                await js.add_stream(name='hello', subjects=['hello'])
                ack = await js.publish('hello', b'Hello JS!')
                print(f'Ack: stream={ack.stream}, sequence={ack.seq}')
                # Ack: stream=hello, sequence=1
                await nc.close()

            if __name__ == '__main__':
                asyncio.run(main())
        """
        ...
    
    def jsm(self, **opts) -> nats.js.JetStreamManager:
        """JetStream context for managing JetStream via JS API"""
        ...
    


