from typing import (
    Any,
    Collection,
    Dict,
    Generic,
    Iterable,
    Iterator,
    List,
    Mapping,
    MutableSet,
    Optional,
    Tuple,
    TypeVar,
    Union,
    overload,
)

from typing_extensions import Protocol

_K = TypeVar("_K")
_V = TypeVar("_V")
_Z = TypeVar("_Z")
_I = TypeVar("_I", covariant=True)

# Unfortunately, there's often check `if isinstance(var, (list, tuple))` in django
# codebase. So we need sometimes to declare exactly list or tuple.
_ListOrTuple = Union[List[_K], Tuple[_K, ...], Tuple[()]]

class _PropertyDescriptor(Generic[_K, _V]):
    """
    This helper property descriptor allows defining asynmetric getter/setters
    which mypy currently doesn't support with either:

        class HttpResponse:
            @property
            def content(...): ...
            @property.setter
            def content(...): ...

    or:

        class HttpResponse:
            def _get_content(...): ...
            def _set_content(...): ...
            content = property(_get_content, _set_content)
    """

    def __get__(self, instance: Any, owner: Optional[Any]) -> _V: ...
    def __set__(self, instance: Any, value: _K) -> None: ...

_IC = TypeVar("_IC", bound="_IndexableCollection")

class _IndexableCollection(Protocol[_I], Collection[_I]):
    @overload
    def __getitem__(self, index: int) -> _I: ...
    @overload
    def __getitem__(self: _IC, index: slice) -> _IC: ...

class OrderedSet(MutableSet[_K]):
    dict: Dict[_K, None] = ...
    def __init__(self, iterable: Optional[Iterable[_K]] = ...) -> None: ...
    def __contains__(self, item: object) -> bool: ...
    def __iter__(self) -> Iterator[_K]: ...
    def __bool__(self) -> bool: ...
    def __len__(self) -> int: ...
    def add(self, item: _K) -> None: ...
    def remove(self, item: _K) -> None: ...
    def discard(self, item: _K) -> None: ...

class MultiValueDictKeyError(KeyError): ...

_D = TypeVar("_D", bound="MultiValueDict")

class MultiValueDict(Dict[_K, _V]):
    @overload
    def __init__(self, key_to_list_mapping: Mapping[_K, Optional[List[_V]]] = ...) -> None: ...
    @overload
    def __init__(self, key_to_list_mapping: Iterable[Tuple[_K, List[_V]]] = ...) -> None: ...
    @overload
    def get(self, key: _K) -> Optional[_V]: ...
    @overload
    def get(self, key: _K, default: _Z = ...) -> Union[_V, _Z]: ...
    def getlist(self, key: _K, default: _Z = ...) -> Union[List[_V], _Z]: ...
    def setlist(self, key: _K, list_: List[_V]) -> None: ...
    def setdefault(self, key: _K, default: _V = ...) -> _V: ...
    def setlistdefault(self, key: _K, default_list: Optional[List[_V]] = ...) -> List[_V]: ...
    def appendlist(self, key: _K, value: _V) -> None: ...
    def items(self) -> Iterator[Tuple[_K, Union[_V, List[object]]]]: ...  # type: ignore
    def lists(self) -> Iterable[Tuple[_K, List[_V]]]: ...
    def dict(self) -> Dict[_K, Union[_V, List[object]]]: ...
    def copy(self: _D) -> _D: ...
    def __getitem__(self, key: _K) -> Union[_V, List[object]]: ...  # type: ignore
    def __setitem__(self, key: _K, value: _V) -> None: ...
    # These overrides are needed to convince mypy that this isn't an abstract class
    def __delitem__(self, item: _K) -> None: ...
    def __len__(self) -> int: ...
    def __iter__(self) -> Iterator[_K]: ...
    # Fake to make `values` work properly
    def values(self) -> Iterator[Union[_V, List[object]]]: ...  # type: ignore[override]

class ImmutableList(Tuple[_V, ...]):
    warning: str = ...
    def __new__(cls, *args: Any, warning: str = ..., **kwargs: Any) -> ImmutableList: ...
    def complain(self, *args: Any, **kwargs: Any) -> None: ...

class _ItemCallable(Protocol[_V]):
    """Don't mess with arguments when assigning in class body in stub"""

    def __call__(self, __value: _V) -> _V: ...

class DictWrapper(Dict[str, _V]):
    func: _ItemCallable[_V] = ...
    prefix: str = ...
    @overload
    def __init__(self, data: Mapping[str, _V], func: _ItemCallable[_V], prefix: str) -> None: ...
    @overload
    def __init__(self, data: Iterable[Tuple[str, _V]], func: _ItemCallable[_V], prefix: str) -> None: ...
    def __getitem__(self, key: str) -> _V: ...

_T = TypeVar("_T", bound="CaseInsensitiveMapping")

class CaseInsensitiveMapping(Mapping[str, _V]):
    _store: Dict[str, Tuple[str, _V]]
    def __init__(self, data: Union[Mapping[str, _V], Iterable[Tuple[str, _V]]]) -> None: ...
    def __getitem__(self, key: str) -> _V: ...
    def __len__(self) -> int: ...
    def __iter__(self) -> Iterator[str]: ...
    def copy(self: _T) -> _T: ...
