"""Tests para cursor_paginate."""

import sys
import os

sys.path.insert(0, os.path.dirname(__file__))

import pytest
from cursor_paginate import cursor_paginate


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


def make_api(pages: list[list[dict]]) -> callable:
    """Return a fetch_page callable that serves pages from a pre-built list."""
    call_count = [0]

    def fetch_page(limit: int, cursor: str | None) -> list[dict]:
        idx = call_count[0]
        call_count[0] += 1
        if idx >= len(pages):
            return []
        return pages[idx][:limit]

    return fetch_page


def get_cursor(item: dict) -> str | None:
    return item.get("cursor")


# ---------------------------------------------------------------------------
# Tests
# ---------------------------------------------------------------------------


def test_api_retorna_3_paginas_de_10_items():
    pages = [
        [{"id": i, "cursor": str(i)} for i in range(0, 10)],
        [{"id": i, "cursor": str(i)} for i in range(10, 20)],
        [{"id": i, "cursor": str(i)} for i in range(20, 30)],
        [],  # sentinel: empty page ends pagination
    ]
    api = make_api(pages)
    result = cursor_paginate(
        fetch_page=api,
        get_cursor=get_cursor,
        page_size=10,
        max_items=2000,
        max_retries=0,
    )
    assert len(result) == 30
    assert result[0]["id"] == 0
    assert result[-1]["id"] == 29


def test_api_falla_1_vez_por_pagina_retry_funciona():
    """fetch_page falla en el primer intento de cada llamada, pero el retry recupera."""
    call_counter = [0]
    # Cada pagina tiene 5 items. 2 paginas en total, luego vacio.
    items_by_page = [
        [{"id": i, "cursor": str(i)} for i in range(0, 5)],
        [{"id": i, "cursor": str(i)} for i in range(5, 10)],
    ]
    page_idx = [0]
    fail_flags = [True, True]  # falla una vez por pagina

    def fetch_page(limit: int, cursor: str | None) -> list[dict]:
        idx = page_idx[0]
        if idx < len(fail_flags) and fail_flags[idx]:
            fail_flags[idx] = False
            raise ConnectionError("transient failure")
        page_idx[0] += 1
        if idx >= len(items_by_page):
            return []
        return items_by_page[idx]

    result = cursor_paginate(
        fetch_page=fetch_page,
        get_cursor=get_cursor,
        page_size=5,
        max_items=2000,
        max_retries=3,
        retry_delay=0.0,
        retryable_exceptions=(ConnectionError, TimeoutError, OSError),
    )
    assert len(result) == 10


def test_max_items_limita_correctamente():
    # 50 items disponibles en 5 paginas de 10, pero max_items=25
    pages = [
        [{"id": i, "cursor": str(i)} for i in range(j * 10, j * 10 + 10)]
        for j in range(5)
    ]
    api = make_api(pages)
    result = cursor_paginate(
        fetch_page=api,
        get_cursor=get_cursor,
        page_size=10,
        max_items=25,
        max_retries=0,
    )
    assert len(result) == 25
    assert result[-1]["id"] == 24


def test_api_retorna_pagina_parcial_ultima_pagina():
    pages = [
        [{"id": i, "cursor": str(i)} for i in range(10)],  # full page
        [{"id": i, "cursor": str(i)} for i in range(10, 17)],  # partial — 7 items
    ]
    api = make_api(pages)
    result = cursor_paginate(
        fetch_page=api,
        get_cursor=get_cursor,
        page_size=10,
        max_items=2000,
        max_retries=0,
    )
    assert len(result) == 17
    assert result[-1]["id"] == 16


def test_cursor_none_en_ultimo_item_se_detiene():
    """Cuando el ultimo item no tiene cursor, la paginacion debe detenerse."""
    pages = [
        [{"id": i, "cursor": str(i)} for i in range(10)],
        # last item has no cursor — signals end of data
        [{"id": i, "cursor": (str(i) if i < 19 else None)} for i in range(10, 20)],
    ]
    api = make_api(pages)

    def get_cursor_nullable(item: dict) -> str | None:
        return item.get("cursor")

    result = cursor_paginate(
        fetch_page=api,
        get_cursor=get_cursor_nullable,
        page_size=10,
        max_items=2000,
        max_retries=0,
    )
    assert len(result) == 20
    assert result[-1]["id"] == 19
