from collections.abc import Callable
from typing import Any, Generic, TypeVar

from django.core.exceptions import ObjectDoesNotExist
from django.db.models.base import Model
from django.db.models.fields import Field
from django.db.models.fields.mixins import FieldCacheMixin
from django.db.models.fields.related import OneToOneField, RelatedField
from django.db.models.fields.reverse_related import ManyToManyRel, OneToOneRel
from django.db.models.query import QuerySet

_T = TypeVar("_T")

class ForwardManyToOneDescriptor:
    RelatedObjectDoesNotExist: type[ObjectDoesNotExist]
    field: Field[Any, Any] = ...
    def __init__(self, field_with_rel: Field[Any, Any]) -> None: ...
    def is_cached(self, instance: Model) -> bool: ...
    def get_queryset(self, **hints: Any) -> QuerySet[Any]: ...
    def get_prefetch_queryset(
        self, instances: list[Model], queryset: QuerySet[Any] | None = ...
    ) -> tuple[
        QuerySet[Any], Callable[..., Any], Callable[..., Any], bool, str, bool
    ]: ...
    def get_object(self, instance: Model) -> Model: ...
    def __get__(
        self, instance: Model | None, cls: type[Model] = ...
    ) -> Model | ForwardManyToOneDescriptor | None: ...
    def __set__(self, instance: Model, value: Model | None) -> None: ...
    def __reduce__(self) -> tuple[Callable[..., Any], tuple[type[Model], str]]: ...

class ForwardOneToOneDescriptor(ForwardManyToOneDescriptor):
    RelatedObjectDoesNotExist: type[ObjectDoesNotExist]
    field: OneToOneField[Any]  # pyright: ignore[reportIncompatibleVariableOverride]
    def get_object(self, instance: Model) -> Model: ...

class ReverseOneToOneDescriptor:
    RelatedObjectDoesNotExist: type[ObjectDoesNotExist]
    related: OneToOneRel = ...
    def __init__(self, related: OneToOneRel) -> None: ...
    def is_cached(self, instance: Model) -> bool: ...
    def get_queryset(self, **hints: Any) -> QuerySet[Any]: ...
    def get_prefetch_queryset(
        self, instances: list[Model], queryset: QuerySet[Any] | None = ...
    ) -> tuple[
        QuerySet[Any], Callable[..., Any], Callable[..., Any], bool, str, bool
    ]: ...
    def __get__(
        self, instance: Model | None, cls: type[Model] = ...
    ) -> Model | ReverseOneToOneDescriptor: ...
    def __set__(self, instance: Model, value: Model | None) -> None: ...
    def __reduce__(self) -> tuple[Callable[..., Any], tuple[type[Model], str]]: ...

class ReverseManyToOneDescriptor:
    rel: FieldCacheMixin = ...
    field: FieldCacheMixin = ...
    def __init__(self, rel: FieldCacheMixin) -> None: ...
    def related_manager_cls(self) -> Any: ...
    def __get__(
        self, instance: Model | None, cls: type[Model] = ...
    ) -> ReverseManyToOneDescriptor: ...
    def __set__(self, instance: Model, value: list[Model]) -> Any: ...

def create_reverse_many_to_one_manager(superclass: Any, rel: Any) -> Any: ...

class ManyToManyDescriptor(ReverseManyToOneDescriptor):
    field: RelatedField[Any, Any]  # pyright: ignore[reportIncompatibleVariableOverride]
    rel: ManyToManyRel  # pyright: ignore[reportIncompatibleVariableOverride]
    reverse: bool = ...
    def __init__(self, rel: ManyToManyRel, reverse: bool = ...) -> None: ...
    @property
    def through(self) -> type[Model]: ...
    def related_manager_cls(self) -> Any: ...

class _ForwardManyToManyManager(Generic[_T]):
    def all(self) -> QuerySet[Any]: ...

def create_forward_many_to_many_manager(
    superclass: Any, rel: Any, reverse: Any
) -> _ForwardManyToManyManager[Any]: ...
