import sys
from collections.abc import Callable
from contextvars import Context
from types import FrameType, TracebackType
from typing import Any, Literal, Protocol, TypeAlias, overload, type_check_only
from typing_extensions import disjoint_base

_TraceEvent: TypeAlias = Literal["switch", "throw"]
_TraceCallback: TypeAlias = Callable[[_TraceEvent, tuple[greenlet, greenlet]], object]

CLOCKS_PER_SEC: int
GREENLET_USE_CONTEXT_VARS: bool
GREENLET_USE_GC: bool
GREENLET_USE_STANDARD_THREADING: bool
GREENLET_USE_TRACING: bool
# this is a PyCapsule, it may be used to pass the gevent C-API to another C-extension
# there isn't a runtime type for this, since it's only an opaque wrapper around void*
# but it's probably still better than pretending it doesn't exist, so people that need
# to pass this around, can still pass it around without having to ignore type errors...
_C_API: object

@type_check_only
class _ParentDescriptor(Protocol):
    def __get__(self, obj: greenlet, owner: type[greenlet] | None = None) -> greenlet | None: ...
    def __set__(self, obj: greenlet, value: greenlet) -> None: ...

class GreenletExit(BaseException): ...
class error(Exception): ...

@disjoint_base
class greenlet:
    @property
    def dead(self) -> bool: ...

    @property
    def gr_context(self) -> Context | None: ...
    @gr_context.setter
    def gr_context(self, value: Context | None) -> None: ...

    @property
    def gr_frame(self) -> FrameType | None: ...
    # the parent attribute is a bit special, since it can't be set to `None` manually, but
    # it can be `None` for the master greenlet which will always be around, regardless of
    # how many greenlets have been spawned explicitly. Since there can only be one such
    # greenlet per thread, there is no way to create another one manually.
    parent: _ParentDescriptor

    @property
    def run(self) -> Callable[..., Any]: ...
    @run.setter
    def run(self, value: Callable[..., Any]) -> None: ...

    def __init__(self, run: Callable[..., Any] | None = None, parent: greenlet | None = None) -> None: ...
    def switch(self, *args: Any, **kwargs: Any) -> Any: ...

    @overload
    def throw(
        self, typ: type[BaseException] = ..., val: BaseException | object = None, tb: TracebackType | None = None, /
    ) -> Any: ...
    @overload
    def throw(self, typ: BaseException = ..., val: None = None, tb: TracebackType | None = None, /) -> Any: ...

    def __bool__(self) -> bool: ...

    # aliases for some module attributes/methods
    GreenletExit: type[GreenletExit]
    error: type[error]
    @staticmethod
    def getcurrent() -> greenlet: ...
    @staticmethod
    def gettrace() -> _TraceCallback | None: ...
    @staticmethod
    def settrace(callback: _TraceCallback | None, /) -> _TraceCallback | None: ...

class UnswitchableGreenlet(greenlet):  # undocumented
    force_switch_error: bool
    force_slp_switch_error: bool

def enable_optional_cleanup(enabled: bool, /) -> None: ...
def get_clocks_used_doing_optional_cleanup() -> int: ...
def get_pending_cleanup_count() -> int: ...
def get_total_main_greenlets() -> int: ...

if sys.version_info < (3, 13):
    def get_tstate_trash_delete_nesting() -> int: ...

def getcurrent() -> greenlet: ...
def gettrace() -> _TraceCallback | None: ...
def set_thread_local(key: object, value: object, /) -> None: ...
def settrace(callback: _TraceCallback | None, /) -> _TraceCallback | None: ...
