import pickle
import sys
from _pickle import _ReducedType
from _typeshed import HasFileno, SupportsWrite, Unused
from abc import ABCMeta
from builtins import type as Type  # alias to avoid name clash
from collections.abc import Callable
from copyreg import _DispatchTableType
from multiprocessing import connection
from socket import socket
from typing import Any, Final

if sys.platform == "win32":
    __all__ = ["send_handle", "recv_handle", "ForkingPickler", "register", "dump", "DupHandle", "duplicate", "steal_handle"]
else:
    __all__ = ["send_handle", "recv_handle", "ForkingPickler", "register", "dump", "DupFd", "sendfds", "recvfds"]

HAVE_SEND_HANDLE: Final[bool]

class ForkingPickler(pickle.Pickler):
    dispatch_table: _DispatchTableType
    def __init__(self, file: SupportsWrite[bytes], protocol: int | None = ...) -> None: ...
    @classmethod
    def register(cls, type: Type, reduce: Callable[[Any], _ReducedType]) -> None: ...
    @classmethod
    def dumps(cls, obj: Any, protocol: int | None = None) -> memoryview: ...
    loads = pickle.loads

register = ForkingPickler.register

def dump(obj: Any, file: SupportsWrite[bytes], protocol: int | None = None) -> None: ...

if sys.platform == "win32":
    def duplicate(
        handle: int, target_process: int | None = None, inheritable: bool = False, *, source_process: int | None = None
    ) -> int: ...
    def steal_handle(source_pid: int, handle: int) -> int: ...
    def send_handle(conn: connection.PipeConnection[DupHandle, Any], handle: int, destination_pid: int) -> None: ...
    def recv_handle(conn: connection.PipeConnection[Any, DupHandle]) -> int: ...

    class DupHandle:
        def __init__(self, handle: int, access: int, pid: int | None = None) -> None: ...
        def detach(self) -> int: ...

else:
    ACKNOWLEDGE: Final[bool]

    def recvfds(sock: socket, size: int) -> list[int]: ...
    def send_handle(conn: HasFileno, handle: int, destination_pid: Unused) -> None: ...
    def recv_handle(conn: HasFileno) -> int: ...
    def sendfds(sock: socket, fds: list[int]) -> None: ...
    def DupFd(fd: int) -> Any: ...  # Return type is really hard to get right

# These aliases are to work around pyright complaints.
# Pyright doesn't like it when a class object is defined as an alias
# of a global object with the same name.
_ForkingPickler = ForkingPickler
_register = register
_dump = dump
_send_handle = send_handle
_recv_handle = recv_handle

if sys.platform == "win32":
    _steal_handle = steal_handle
    _duplicate = duplicate
    _DupHandle = DupHandle
else:
    _sendfds = sendfds
    _recvfds = recvfds
    _DupFd = DupFd

class AbstractReducer(metaclass=ABCMeta):
    ForkingPickler = _ForkingPickler
    register = _register
    dump = _dump
    send_handle = _send_handle
    recv_handle = _recv_handle
    if sys.platform == "win32":
        steal_handle = _steal_handle
        duplicate = _duplicate
        DupHandle = _DupHandle
    else:
        sendfds = _sendfds
        recvfds = _recvfds
        DupFd = _DupFd

    def __init__(self, *args: Unused) -> None: ...
