from collections.abc import AsyncIterator, Iterable, Iterator, Sequence, Sized
from typing import ClassVar, Generic, Protocol, TypeVar, overload, type_check_only

from django.db.models.fields import _ErrorMessagesDict
from django.utils.functional import _StrPromise, cached_property
from typing_extensions import override

class UnorderedObjectListWarning(RuntimeWarning): ...
class InvalidPage(Exception): ...
class PageNotAnInteger(InvalidPage): ...
class EmptyPage(InvalidPage): ...

_T = TypeVar("_T")

@type_check_only
class _SupportsPagination(Protocol[_T], Sized, Iterable):
    @overload
    def __getitem__(self, index: int, /) -> _T: ...
    @overload
    def __getitem__(self, index: slice, /) -> _SupportsPagination[_T]: ...

class BasePaginator(Generic[_T]):
    ELLIPSIS: ClassVar[_StrPromise]
    default_error_messages: ClassVar[_ErrorMessagesDict]
    error_messages: _ErrorMessagesDict
    object_list: _SupportsPagination[_T]
    per_page: int
    orphans: int
    allow_empty_first_page: bool
    def __init__(
        self,
        object_list: _SupportsPagination[_T],
        per_page: int | str,
        orphans: int = 0,
        allow_empty_first_page: bool = True,
        error_messages: _ErrorMessagesDict | None = None,
    ) -> None: ...

class Paginator(BasePaginator[_T]):
    def __iter__(self) -> Iterator[Page[_T]]: ...
    def validate_number(self, number: int | float | str) -> int: ...
    def get_page(self, number: int | float | str | None) -> Page[_T]: ...
    def page(self, number: int | str) -> Page[_T]: ...
    @cached_property
    def count(self) -> int: ...
    @cached_property
    def num_pages(self) -> int: ...
    @property
    def page_range(self) -> range: ...
    def get_elided_page_range(
        self, number: int | float | str = 1, *, on_each_side: int = 3, on_ends: int = 2
    ) -> Iterator[str | int]: ...

class AsyncPaginator(BasePaginator[_T]):
    def __aiter__(self) -> AsyncIterator[AsyncPage[_T]]: ...
    async def avalidate_number(self, number: int | float | str) -> int: ...
    async def aget_page(self, number: int | float | str | None) -> AsyncPage[_T]: ...
    async def apage(self, number: int | str) -> AsyncPage[_T]: ...
    async def acount(self) -> int: ...
    async def anum_pages(self) -> int: ...
    async def apage_range(self) -> range: ...
    async def aget_elided_page_range(
        self, number: int | float | str = 1, *, on_each_side: int = 3, on_ends: int = 2
    ) -> AsyncIterator[str | int]: ...

class Page(Sequence[_T]):
    object_list: _SupportsPagination[_T]
    number: int
    paginator: Paginator[_T]
    def __init__(
        self,
        object_list: _SupportsPagination[_T],
        number: int,
        paginator: Paginator[_T],
    ) -> None: ...
    @overload
    @override
    def __getitem__(self, index: int) -> _T: ...
    @overload
    @override
    def __getitem__(self, index: slice) -> Sequence[_T]: ...  # Avoid override error
    @override
    def __len__(self) -> int: ...
    def has_next(self) -> bool: ...
    def has_previous(self) -> bool: ...
    def has_other_pages(self) -> bool: ...
    def next_page_number(self) -> int: ...
    def previous_page_number(self) -> int: ...
    def start_index(self) -> int: ...
    def end_index(self) -> int: ...

class AsyncPage(Generic[_T]):
    object_list: _SupportsPagination[_T]
    number: int
    paginator: AsyncPaginator[_T]
    def __init__(
        self,
        object_list: _SupportsPagination[_T],
        number: int,
        paginator: AsyncPaginator[_T],
    ) -> None: ...
    def __aiter__(self) -> AsyncIterator[_T]: ...
    def __len__(self) -> int: ...
    def __reversed__(self) -> Iterator[_T]: ...
    @overload
    def __getitem__(self, index: int) -> _T: ...
    @overload
    def __getitem__(self, index: slice) -> list[_T]: ...
    async def aget_object_list(self) -> list[_T]: ...
    async def ahas_next(self) -> bool: ...
    async def ahas_previous(self) -> bool: ...
    async def ahas_other_pages(self) -> bool: ...
    async def anext_page_number(self) -> int: ...
    async def aprevious_page_number(self) -> int: ...
    async def astart_index(self) -> int: ...
    async def aend_index(self) -> int: ...
