import datetime
from typing import Any, Dict, Mapping, Optional, Tuple

from bson.objectid import ObjectId
from pymongo.ismaster import IsMaster
from pymongo.server_description import ServerDescription
from pymongo.topology_description import TopologyDescription

class _EventListener: ...

class CommandListener(_EventListener):
    def started(self, event: CommandStartedEvent) -> None: ...
    def succeeded(self, event: CommandSucceededEvent) -> None: ...
    def failed(self, event: CommandFailedEvent) -> None: ...

class ConnectionPoolListener(_EventListener):
    def pool_created(self, event: PoolCreatedEvent) -> None: ...
    def pool_cleared(self, event: PoolClearedEvent) -> None: ...
    def pool_closed(self, event: PoolClosedEvent) -> None: ...
    def connection_created(self, event: ConnectionCreatedEvent) -> None: ...
    def connection_ready(self, event: ConnectionReadyEvent) -> None: ...
    def connection_closed(self, event: ConnectionClosedEvent) -> None: ...
    def connection_check_out_started(self, event: ConnectionCheckOutStartedEvent) -> None: ...
    def connection_check_out_failed(self, event: ConnectionCheckOutFailedEvent) -> None: ...
    def connection_checked_out(self, event: ConnectionCheckedOutEvent) -> None: ...
    def connection_checked_in(self, event: ConnectionCheckedInEvent) -> None: ...

class ServerHeartbeatListener(_EventListener):
    def started(self, event: ServerHeartbeatStartedEvent) -> None: ...
    def succeeded(self, event: ServerHeartbeatSucceededEvent) -> None: ...
    def failed(self, event: ServerHeartbeatFailedEvent) -> None: ...

class TopologyListener(_EventListener):
    def opened(self, event: TopologyOpenedEvent) -> None: ...
    def description_changed(self, event: TopologyDescriptionChangedEvent) -> None: ...
    def closed(self, event: TopologyClosedEvent) -> None: ...

class ServerListener(_EventListener):
    def opened(self, event: ServerOpeningEvent) -> None: ...
    def description_changed(self, event: ServerDescriptionChangedEvent) -> None: ...
    def closed(self, event: ServerClosedEvent) -> None: ...

def register(listener: _EventListener) -> None: ...

_Document = Mapping[str, Any]
_Address = Tuple[str, Optional[int]]

class _CommandEvent:
    def __init__(
        self,
        command_name: str,
        request_id: int,
        connection_id: _Address,
        operation_id: Optional[int],
        service_id: Optional[ObjectId] = ...,
    ) -> None: ...
    @property
    def command_name(self) -> str: ...
    @property
    def request_id(self) -> int: ...
    @property
    def connection_id(self) -> _Address: ...
    @property
    def operation_id(self) -> Optional[int]: ...

class CommandStartedEvent(_CommandEvent):
    def __init__(
        self,
        command: _Document,
        database_name: str,
        request_id: int,
        connection_id: _Address,
        operation_id: Optional[int],
        service_id: Optional[ObjectId] = ...,
    ) -> None: ...
    @property
    def command(self) -> _Document: ...
    @property
    def database_name(self) -> str: ...

class CommandSucceededEvent(_CommandEvent):
    def __init__(
        self,
        duration: datetime.timedelta,
        reply: _Document,
        command_name: str,
        request_id: int,
        connection_id: _Address,
        operation_id: Optional[int],
        service_id: Optional[ObjectId] = ...,
    ) -> None: ...
    @property
    def duration_micros(self) -> int: ...
    @property
    def reply(self) -> _Document: ...

class CommandFailedEvent(_CommandEvent):
    def __init__(
        self,
        duration: datetime.timedelta,
        failure: _Document,
        command_name: str,
        request_id: int,
        connection_id: _Address,
        operation_id: Optional[int],
        service_id: Optional[ObjectId] = ...,
    ) -> None: ...
    @property
    def duration_micros(self) -> int: ...
    @property
    def failure(self) -> _Document: ...

class _PoolEvent:
    def __init__(self, address: _Address) -> None: ...
    @property
    def address(self) -> _Address: ...

class PoolCreatedEvent(_PoolEvent):
    def __init__(self, address: _Address, options: Dict[str, Any]) -> None: ...
    @property
    def options(self) -> Dict[str, Any]: ...

class PoolClearedEvent(_PoolEvent):
    def __init__(self, address: _Address, service_id: Optional[ObjectId] = ...) -> None: ...

class PoolClosedEvent(_PoolEvent): ...

class ConnectionClosedReason:
    STALE: str = ...
    IDLE: str = ...
    ERROR: str = ...
    POOL_CLOSED: str = ...

class ConnectionCheckOutFailedReason:
    TIMEOUT: str = ...
    POOL_CLOSED: str = ...
    CONN_ERROR: str = ...

class _ConnectionEvent:
    def __init__(self, address: _Address, connection_id: int) -> None: ...
    @property
    def address(self) -> _Address: ...
    @property
    def connection_id(self) -> int: ...

class ConnectionCreatedEvent(_ConnectionEvent): ...
class ConnectionReadyEvent(_ConnectionEvent): ...

class ConnectionClosedEvent(_ConnectionEvent):
    def __init__(self, address: _Address, connection_id: int, reason: str) -> None: ...
    @property
    def reason(self) -> str: ...

class ConnectionCheckOutStartedEvent:
    def __init__(self, address: _Address) -> None: ...
    @property
    def address(self) -> _Address: ...

class ConnectionCheckOutFailedEvent:
    def __init__(self, address: _Address, reason: str) -> None: ...
    @property
    def address(self) -> _Address: ...
    @property
    def reason(self) -> str: ...

class ConnectionCheckedOutEvent(_ConnectionEvent): ...
class ConnectionCheckedInEvent(_ConnectionEvent): ...

class _ServerEvent:
    def __init__(self, server_address: _Address, topology_id: ObjectId) -> None: ...
    @property
    def server_address(self) -> _Address: ...
    @property
    def topology_id(self) -> ObjectId: ...

class ServerDescriptionChangedEvent(_ServerEvent):
    def __init__(self, previous_description: ServerDescription, new_description: ServerDescription, *args: Any) -> None: ...
    @property
    def previous_description(self) -> ServerDescription: ...
    @property
    def new_description(self) -> ServerDescription: ...

class ServerOpeningEvent(_ServerEvent): ...
class ServerClosedEvent(_ServerEvent): ...

class TopologyEvent:
    def __init__(self, topology_id: ObjectId) -> None: ...
    @property
    def topology_id(self) -> ObjectId: ...

class TopologyDescriptionChangedEvent(TopologyEvent):
    def __init__(self, previous_description: TopologyDescription, new_description: TopologyDescription, *args: Any) -> None: ...
    @property
    def previous_description(self) -> TopologyDescription: ...
    @property
    def new_description(self) -> TopologyDescription: ...

class TopologyOpenedEvent(TopologyEvent): ...
class TopologyClosedEvent(TopologyEvent): ...

class _ServerHeartbeatEvent:
    def __init__(self, connection_id: _Address) -> None: ...
    @property
    def connection_id(self) -> _Address: ...

class ServerHeartbeatStartedEvent(_ServerHeartbeatEvent): ...

class ServerHeartbeatSucceededEvent(_ServerHeartbeatEvent):
    def __init__(self, duration: float, reply: IsMaster, connection_id: _Address, awaited: bool = ...) -> None: ...
    @property
    def duration(self) -> float: ...
    @property
    def reply(self) -> IsMaster: ...
    @property
    def awaited(self) -> bool: ...

class ServerHeartbeatFailedEvent(_ServerHeartbeatEvent):
    def __init__(self, duration: float, reply: Exception, connection_id: _Address, awaited: bool = ...) -> None: ...
    @property
    def duration(self) -> int: ...
    @property
    def reply(self) -> Exception: ...
    @property
    def awaited(self) -> bool: ...
