from _typeshed import FileDescriptor
from collections.abc import Callable, Collection, Iterable
from types import TracebackType
from typing import Any, Generic, Protocol, TypeVar, overload, type_check_only
from typing_extensions import Self, TypeVarTuple, Unpack, disjoint_base

from gevent._greenlet_primitives import SwitchOutGreenletWithLoop
from gevent._types import _Loop, _Watcher
from gevent.hub import Hub
from gevent.socket import socket

__all__ = ["WaitOperationsGreenlet", "iwait_on_objects", "wait_on_objects", "wait_read", "wait_write", "wait_readwrite"]

_T = TypeVar("_T")
_Ts = TypeVarTuple("_Ts")
_WaitableT = TypeVar("_WaitableT", bound=_Waitable)

@type_check_only
class _Waitable(Protocol):
    def rawlink(self, callback: Callable[[Any], object], /) -> object: ...
    def unlink(self, callback: Callable[[Any], object], /) -> object: ...

class WaitOperationsGreenlet(SwitchOutGreenletWithLoop):
    loop: _Loop
    def wait(self, watcher: _Watcher) -> None: ...
    def cancel_waits_close_and_then(
        self,
        watchers: Iterable[_Watcher],
        exc_kind: type[BaseException] | BaseException,
        then: Callable[[Unpack[_Ts]], object],
        *then_args: Unpack[_Ts],
    ) -> None: ...
    def cancel_wait(self, watcher: _Watcher, error: type[BaseException] | BaseException, close_watcher: bool = False) -> None: ...

@disjoint_base
class _WaitIterator(Generic[_T]):
    def __init__(self, objects: Collection[_T], hub: Hub, timeout: float, count: None | int) -> None: ...
    def __enter__(self) -> Self: ...
    def __exit__(self, typ: type[BaseException] | None, value: BaseException | None, tb: TracebackType | None) -> None: ...
    def __iter__(self) -> Self: ...
    def __next__(self) -> _T: ...
    next = __next__

@overload
def iwait_on_objects(objects: None, timeout: float | None = None, count: int | None = None) -> list[bool]: ...
@overload
def iwait_on_objects(
    objects: Collection[_WaitableT], timeout: float | None = None, count: int | None = None
) -> _WaitIterator[_WaitableT]: ...

@overload
def wait_on_objects(objects: None = None, timeout: float | None = None, count: int | None = None) -> bool: ...
@overload
def wait_on_objects(
    objects: Collection[_WaitableT], timeout: float | None = None, count: int | None = None
) -> list[_WaitableT]: ...

def set_default_timeout_error(e: type[BaseException]) -> None: ...
def wait_on_socket(socket: socket, watcher: _Watcher, timeout_exc: type[BaseException] | BaseException | None = None) -> None: ...
def wait_on_watcher(
    watcher: _Watcher,
    timeout: float | None = None,
    timeout_exc: type[BaseException] | BaseException = ...,
    hub: Hub | None = None,
) -> None: ...
def wait_read(
    fileno: FileDescriptor, timeout: float | None = None, timeout_exc: type[BaseException] | BaseException = ...
) -> None: ...
def wait_write(
    fileno: FileDescriptor, timeout: float | None = None, timeout_exc: type[BaseException] | BaseException = ...
) -> None: ...
def wait_readwrite(
    fileno: FileDescriptor, timeout: float | None = None, timeout_exc: type[BaseException] | BaseException = ...
) -> None: ...
