from typing import Any, Dict, FrozenSet, List, Mapping, MutableMapping, Optional, Sequence, Set, Tuple, Type, Union

from bson.codec_options import CodecOptions, TypeRegistry
from bson.raw_bson import RawBSONDocument
from pymongo import common
from pymongo.change_stream import ChangeStream
from pymongo.client_session import ClientSession, TransactionOptions
from pymongo.collation import Collation
from pymongo.command_cursor import CommandCursor
from pymongo.cursor_manager import CursorManager
from pymongo.database import Database
from pymongo.monitoring import _EventListener
from pymongo.read_concern import ReadConcern
from pymongo.read_preferences import _ServerMode
from pymongo.write_concern import WriteConcern

_Pipeline = List[Mapping[str, Any]]
_Collation = Union[Mapping[str, Any], Collation]

class MongoClient(common.BaseObject):
    HOST: str = ...
    PORT: int = ...
    def __init__(
        self,
        host: Optional[Union[str, List[str]]] = ...,
        port: Optional[int] = ...,
        document_class: Optional[Union[Type[MutableMapping], Type[RawBSONDocument]]] = ...,
        tz_aware: Optional[bool] = ...,
        connect: Optional[bool] = ...,
        type_registry: Optional[TypeRegistry] = ...,
        **kwargs: Any,
    ) -> None: ...
    def watch(
        self,
        pipeline: Optional[_Pipeline] = ...,
        full_document: Optional[bool] = ...,
        resume_after: Optional[Mapping[str, Any]] = ...,
        max_await_time_ms: Optional[int] = ...,
        batch_size: Optional[int] = ...,
        collation: Optional[_Collation] = ...,
        start_at_operation_time: Optional[Mapping[str, Any]] = ...,
        session: Optional[ClientSession] = ...,
        start_after: Optional[Mapping[str, Any]] = ...,
    ) -> ChangeStream: ...
    @property
    def event_listeners(self) -> Tuple[List[_EventListener], ...]: ...
    @property
    def address(self) -> Optional[Tuple[str, int]]: ...
    @property
    def primary(self) -> Optional[Tuple[str, int]]: ...
    @property
    def secondaries(self) -> Set[Tuple[str, int]]: ...
    @property
    def arbiters(self) -> Set[Tuple[str, int]]: ...
    @property
    def is_primary(self) -> bool: ...
    @property
    def is_mongos(self) -> bool: ...
    @property
    def max_pool_size(self) -> Optional[int]: ...
    @property
    def min_pool_size(self) -> int: ...
    @property
    def max_idle_time_ms(self) -> Optional[int]: ...
    @property
    def nodes(self) -> FrozenSet[Tuple[str, Optional[int]]]: ...
    @property
    def max_bson_size(self) -> int: ...
    @property
    def max_message_size(self) -> int: ...
    @property
    def max_write_batch_size(self) -> int: ...
    @property
    def local_threshold_ms(self) -> float: ...
    @property
    def server_selection_timeout(self) -> float: ...
    @property
    def retry_writes(self) -> bool: ...
    @property
    def retry_reads(self) -> bool: ...
    def close(self) -> None: ...
    def set_cursor_manager(self, manager_class: CursorManager) -> None: ...
    def __eq__(self, other: Any) -> bool: ...
    def __ne__(self, other: Any) -> bool: ...
    def __getattr__(self, name: str) -> Database: ...
    def __getitem__(self, name: str) -> Database: ...
    def close_cursor(self, cursor_id: int, address: Optional[Tuple[str, Optional[int]]] = ...) -> None: ...
    def kill_cursors(self, cursor_ids: Sequence[int], address: Optional[Tuple[str, Optional[int]]] = ...) -> None: ...
    def start_session(
        self,
        causal_consistency: Optional[bool] = ...,
        default_transaction_options: Optional[TransactionOptions] = ...,
        snapshot: Optional[bool] = ...,
    ) -> ClientSession: ...
    def server_info(self, session: Optional[ClientSession] = ...) -> Dict[str, Any]: ...
    def list_databases(self, session: Optional[ClientSession] = ..., **kwargs: Any) -> CommandCursor: ...
    def list_database_names(self, session: Optional[ClientSession] = ...) -> List[str]: ...
    def database_names(self, session: Optional[ClientSession] = ...) -> List[str]: ...
    def drop_database(self, name_or_database: Union[str, Database], session: Optional[ClientSession] = ...) -> None: ...
    def get_default_database(
        self,
        default: Optional[str] = ...,
        codec_options: Optional[CodecOptions] = ...,
        read_preference: Optional[_ServerMode] = ...,
        write_concern: Optional[WriteConcern] = ...,
        read_concern: Optional[ReadConcern] = ...,
    ) -> Database: ...
    def get_database(
        self,
        name: Optional[str] = ...,
        codec_options: Optional[CodecOptions] = ...,
        read_preference: Optional[_ServerMode] = ...,
        write_concern: Optional[WriteConcern] = ...,
        read_concern: Optional[ReadConcern] = ...,
    ) -> Database: ...
    @property
    def is_locked(self) -> bool: ...
    def fsync(self, **kwargs: Any) -> Dict[str, Any]: ...
    def unlock(self, session: Optional[ClientSession] = ...) -> None: ...
    def __enter__(self) -> MongoClient: ...
    def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: ...
