"""Pagination helpers for CoinCircuit list endpoints."""

from typing import Any, AsyncIterator, Dict, Iterator, List, Optional, TypeVar

from .types import PaginationMetaDto as PaginationMeta

T = TypeVar("T")


class PaginatedResponse:
    """Wraps a paginated API response with data items and pagination metadata.

    Attributes:
        data: The list of items for the current page.
        meta: Pagination metadata (page, size, total, totalPages).
    """

    def __init__(self, data: List[Any], meta: PaginationMeta) -> None:
        self.data = data
        self.meta = meta

    @property
    def page(self) -> int:
        return self.meta["page"]

    @property
    def size(self) -> int:
        return self.meta["size"]

    @property
    def total(self) -> int:
        return self.meta["total"]

    @property
    def total_pages(self) -> int:
        return self.meta["totalPages"]

    @property
    def has_next_page(self) -> bool:
        return self.page < self.total_pages

    def __iter__(self) -> Iterator[Any]:
        return iter(self.data)

    def __len__(self) -> int:
        return len(self.data)

    def __repr__(self) -> str:
        return (
            f"PaginatedResponse(page={self.page}, size={self.size}, "
            f"total={self.total}, items={len(self.data)})"
        )


def paginate(
    request_fn: Any,
    params: Optional[Dict[str, Any]] = None,
) -> Iterator[Any]:
    """Synchronous auto-paginating iterator.

    Yields each item across all pages by calling ``request_fn`` repeatedly
    until there are no more pages.

    Args:
        request_fn: A callable that accepts ``**params`` and returns a
            ``PaginatedResponse``.
        params: Query parameters to pass. ``page`` is managed automatically.
    """
    params = dict(params or {})
    page = params.pop("page", 1)

    while True:
        params["page"] = page
        response: PaginatedResponse = request_fn(**params)
        for item in response.data:
            yield item
        if not response.has_next_page:
            break
        page += 1


async def async_paginate(
    request_fn: Any,
    params: Optional[Dict[str, Any]] = None,
) -> AsyncIterator[Any]:
    """Asynchronous auto-paginating iterator.

    Same as ``paginate`` but for async resource methods.
    """
    params = dict(params or {})
    page = params.pop("page", 1)

    while True:
        params["page"] = page
        response: PaginatedResponse = await request_fn(**params)
        for item in response.data:
            yield item
        if not response.has_next_page:
            break
        page += 1
