from collections.abc import Callable
from collections.abc import Hashable
from collections.abc import Iterable
from typing import Any
from typing import NoReturn
from typing import overload
from typing import SupportsIndex
from typing import TypeVar

from _typeshed import SupportsKeysAndGetItem

from .headers import Headers

K = TypeVar("K")
T = TypeVar("T")
V = TypeVar("V")

def is_immutable(self: object) -> NoReturn: ...

class ImmutableListMixin(list[V]):
    _hash_cache: int | None
    def __hash__(self) -> int: ...  # type: ignore
    def __delitem__(self, key: SupportsIndex | slice) -> NoReturn: ...
    def __iadd__(self, other: t.Any) -> NoReturn: ...  # type: ignore
    def __imul__(self, other: SupportsIndex) -> NoReturn: ...
    def __setitem__(self, key: int | slice, value: V) -> NoReturn: ...  # type: ignore
    def append(self, value: V) -> NoReturn: ...
    def remove(self, value: V) -> NoReturn: ...
    def extend(self, values: Iterable[V]) -> NoReturn: ...
    def insert(self, pos: SupportsIndex, value: V) -> NoReturn: ...
    def pop(self, index: SupportsIndex = -1) -> NoReturn: ...
    def reverse(self) -> NoReturn: ...
    def sort(
        self, key: Callable[[V], Any] | None = None, reverse: bool = False
    ) -> NoReturn: ...

class ImmutableDictMixin(dict[K, V]):
    _hash_cache: int | None
    @classmethod
    def fromkeys(  # type: ignore
        cls, keys: Iterable[K], value: V | None = None
    ) -> ImmutableDictMixin[K, V]: ...
    def _iter_hashitems(self) -> Iterable[Hashable]: ...
    def __hash__(self) -> int: ...  # type: ignore
    def setdefault(self, key: K, default: V | None = None) -> NoReturn: ...
    def update(self, *args: Any, **kwargs: V) -> NoReturn: ...
    def pop(self, key: K, default: V | None = None) -> NoReturn: ...  # type: ignore
    def popitem(self) -> NoReturn: ...
    def __setitem__(self, key: K, value: V) -> NoReturn: ...
    def __delitem__(self, key: K) -> NoReturn: ...
    def clear(self) -> NoReturn: ...

class ImmutableMultiDictMixin(ImmutableDictMixin[K, V]):
    def _iter_hashitems(self) -> Iterable[Hashable]: ...
    def add(self, key: K, value: V) -> NoReturn: ...
    def popitemlist(self) -> NoReturn: ...
    def poplist(self, key: K) -> NoReturn: ...
    def setlist(self, key: K, new_list: Iterable[V]) -> NoReturn: ...
    def setlistdefault(
        self, key: K, default_list: Iterable[V] | None = None
    ) -> NoReturn: ...

class ImmutableHeadersMixin(Headers):
    def __delitem__(self, key: Any, _index_operation: bool = True) -> NoReturn: ...
    def __setitem__(self, key: Any, value: Any) -> NoReturn: ...
    def set(self, _key: Any, _value: Any, **kw: Any) -> NoReturn: ...
    def setlist(self, key: Any, values: Any) -> NoReturn: ...
    def add(self, _key: Any, _value: Any, **kw: Any) -> NoReturn: ...
    def add_header(self, _key: Any, _value: Any, **_kw: Any) -> NoReturn: ...
    def remove(self, key: Any) -> NoReturn: ...
    def extend(self, *args: Any, **kwargs: Any) -> NoReturn: ...
    def update(self, *args: Any, **kwargs: Any) -> NoReturn: ...
    def insert(self, pos: Any, value: Any) -> NoReturn: ...
    def pop(self, key: Any = None, default: Any = ...) -> NoReturn: ...
    def popitem(self) -> NoReturn: ...
    def setdefault(self, key: Any, default: Any) -> NoReturn: ...
    def setlistdefault(self, key: Any, default: Any) -> NoReturn: ...

def _calls_update(name: str) -> Callable[[UpdateDictMixin[K, V]], Any]: ...

class UpdateDictMixin(dict[K, V]):
    on_update: Callable[[UpdateDictMixin[K, V] | None, None], None]
    def setdefault(self, key: K, default: V | None = None) -> V: ...
    @overload
    def pop(self, key: K) -> V: ...
    @overload
    def pop(self, key: K, default: V | T = ...) -> V | T: ...
    def __setitem__(self, key: K, value: V) -> None: ...
    def __delitem__(self, key: K) -> None: ...
    def clear(self) -> None: ...
    def popitem(self) -> tuple[K, V]: ...
    @overload
    def update(self, __m: SupportsKeysAndGetItem[K, V], **kwargs: V) -> None: ...
    @overload
    def update(self, __m: Iterable[tuple[K, V]], **kwargs: V) -> None: ...
    @overload
    def update(self, **kwargs: V) -> None: ...
