import threading
import types
from collections.abc import Callable, Iterable, Iterator
from pathlib import Path
from typing import Any

from django.apps.registry import Apps
from django.dispatch import Signal
from django.utils._os import _PathCompatible
from typing_extensions import ParamSpec

_P = ParamSpec("_P")

autoreload_started: Signal
file_changed: Signal
DJANGO_AUTORELOAD_ENV: str

def is_django_module(module: types.ModuleType) -> bool: ...
def is_django_path(path: _PathCompatible) -> bool: ...
def check_errors(fn: Callable[_P, Any]) -> Callable[_P, None]: ...
def raise_last_exception() -> None: ...
def ensure_echo_on() -> None: ...
def iter_all_python_module_files() -> set[Path]: ...
def iter_modules_and_files(
    modules: Iterable[types.ModuleType], extra_files: Iterable[_PathCompatible]
) -> frozenset[Path]: ...
def common_roots(paths: Iterable[Path]) -> Iterable[Path]: ...
def sys_path_directories() -> Iterator[Path]: ...
def get_child_arguments() -> list[str]: ...
def trigger_reload(filename: str) -> None: ...
def restart_with_reloader() -> int: ...

class BaseReloader:
    extra_files: set[Path]
    directory_globs: dict[Path, set[str]]
    def __init__(self) -> None: ...
    def watch_dir(self, path: _PathCompatible, glob: str) -> None: ...
    def watched_files(self, include_globs: bool = ...) -> Iterator[Path]: ...
    def wait_for_apps_ready(self, app_reg: Apps, django_main_thread: threading.Thread) -> bool: ...
    def run(self, django_main_thread: threading.Thread) -> None: ...
    def run_loop(self) -> None: ...
    def tick(self) -> Iterator[None]: ...
    @classmethod
    def check_availability(cls) -> bool | None: ...
    def notify_file_changed(self, path: _PathCompatible) -> None: ...
    @property
    def should_stop(self) -> bool: ...
    def stop(self) -> None: ...

class StatReloader(BaseReloader):
    SLEEP_TIME: int
    def snapshot_files(self) -> Iterator[tuple[Path, float]]: ...
    @classmethod
    def check_availability(cls) -> bool: ...

class WatchmanUnavailable(RuntimeError): ...

class WatchmanReloader(BaseReloader):
    processed_request: threading.Event
    client_timeout: int
    def __init__(self) -> None: ...
    @property
    def client(self) -> Any: ...
    def watched_roots(self, watched_files: Iterable[Path]) -> frozenset[Path]: ...
    def update_watches(self) -> None: ...
    def request_processed(self, **kwargs: Any) -> None: ...
    def check_server_status(self, inner_ex: BaseException | None = ...) -> bool: ...
    @classmethod
    def check_availability(cls) -> None: ...
    def stop(self) -> None: ...

def get_reloader() -> BaseReloader: ...
def start_django(reloader: BaseReloader, main_func: Callable, *args: Any, **kwargs: Any) -> None: ...
def run_with_reloader(main_func: Callable, *args: Any, **kwargs: Any) -> None: ...
