from typing import Any, Callable, Dict, List, Optional, Tuple, Type, Union

from django.db.models.base import Model
from django.db.models.fields import AutoField, Field
from django.db.models.fields.related import ForeignKey, OneToOneField, RelatedField
from django.db.models.lookups import BuiltinLookup, StartsWith
from django.db.models.query_utils import FilteredRelation, PathInfo, Q
from django.db.models.sql.where import WhereNode

from .mixins import FieldCacheMixin

class ForeignObjectRel(FieldCacheMixin):
    many_to_many: bool
    many_to_one: bool
    one_to_many: bool
    one_to_one: bool
    auto_created: bool = ...
    concrete: bool = ...
    editable: bool = ...
    is_relation: bool = ...
    null: bool = ...
    field: RelatedField = ...
    model: Union[Type[Model], str] = ...
    related_name: Optional[str] = ...
    related_query_name: Optional[str] = ...
    limit_choices_to: Optional[Union[Dict[str, Any], Callable[[], Any]]] = ...
    parent_link: bool = ...
    on_delete: Callable = ...
    symmetrical: bool = ...
    multiple: bool = ...
    field_name: Optional[str] = ...
    def __init__(
        self,
        field: RelatedField,
        to: Union[Type[Model], str],
        related_name: Optional[str] = ...,
        related_query_name: Optional[str] = ...,
        limit_choices_to: Optional[Union[Dict[str, Any], Callable[[], Any]]] = ...,
        parent_link: bool = ...,
        on_delete: Optional[Callable] = ...,
    ) -> None: ...
    @property
    def hidden(self) -> bool: ...
    @property
    def name(self) -> str: ...
    @property
    def related_model(self) -> Type[Model]: ...
    @property
    def remote_field(self) -> RelatedField: ...
    @property
    def target_field(self) -> AutoField: ...
    def get_lookup(self, lookup_name: str) -> Type[BuiltinLookup]: ...
    def get_internal_type(self) -> str: ...
    @property
    def db_type(self) -> Callable: ...
    def get_choices(
        self, include_blank: bool = ..., blank_choice: List[Tuple[str, str]] = ...
    ) -> List[Tuple[int, str]]: ...
    def is_hidden(self) -> bool: ...
    def get_joining_columns(self) -> Tuple: ...
    def get_extra_restriction(
        self, where_class: Type[WhereNode], alias: str, related_alias: str
    ) -> Optional[Union[StartsWith, WhereNode]]: ...
    def set_field_name(self) -> None: ...
    def get_accessor_name(self, model: Optional[Type[Model]] = ...) -> Optional[str]: ...
    def get_path_info(self, filtered_relation: Optional[FilteredRelation] = ...) -> List[PathInfo]: ...

class ManyToOneRel(ForeignObjectRel):
    def __init__(
        self,
        field: ForeignKey,
        to: Union[Type[Model], str],
        field_name: Optional[str],
        related_name: Optional[str] = ...,
        related_query_name: Optional[str] = ...,
        limit_choices_to: Optional[Union[Dict[str, Any], Callable[[], Any]]] = ...,
        parent_link: bool = ...,
        on_delete: Callable = ...,
    ) -> None: ...
    def get_related_field(self) -> Field: ...

class OneToOneRel(ManyToOneRel):
    def __init__(
        self,
        field: OneToOneField,
        to: Union[Type[Model], str],
        field_name: Optional[str],
        related_name: Optional[str] = ...,
        related_query_name: Optional[str] = ...,
        limit_choices_to: Optional[Dict[str, str]] = ...,
        parent_link: bool = ...,
        on_delete: Callable = ...,
    ) -> None: ...

class ManyToManyRel(ForeignObjectRel):
    through: Optional[Union[Type[Model], str]] = ...
    through_fields: Optional[Tuple[str, str]] = ...
    db_constraint: bool = ...
    def __init__(
        self,
        field: RelatedField,
        to: Union[Type[Model], str],
        related_name: Optional[str] = ...,
        related_query_name: Optional[str] = ...,
        limit_choices_to: Any = ...,
        symmetrical: bool = ...,
        through: Optional[Union[Type[Model], str]] = ...,
        through_fields: Optional[Tuple[str, str]] = ...,
        db_constraint: bool = ...,
    ) -> None: ...
    def get_related_field(self) -> Field: ...
