import threading
import types
from collections.abc import Callable, Iterable, Iterator
from logging import Logger
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")

logger: Logger
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 = True) -> 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 = 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: ...
