"""Tests for auto-pagination."""

import httpx

from goodmem.pagination import Paginator
from goodmem.registries.convenience import _transform_list_kwargs


class FakeItem:
    def __init__(self, name: str):
        self.name = name


class FakePage:
    def __init__(self, items: list | None, next_token: str | None = None):
        self.items = items
        self.next_token = next_token

    @classmethod
    def model_validate(cls, data: dict) -> "FakePage":
        raw_items = data.get("items")
        items = [FakeItem(i) for i in raw_items] if raw_items else raw_items
        return cls(items=items, next_token=data.get("nextToken"))


def _make_fetch_fn(pages: list[dict]):
    """Create a fetch function that returns pages in sequence."""
    call_count = [0]

    def fetch(token: str | None) -> httpx.Response:
        idx = call_count[0]
        call_count[0] += 1
        return httpx.Response(200, json=pages[idx])

    return fetch


def test_single_page_iteration():
    pages = [{"items": ["a", "b", "c"]}]
    paginator = Paginator(
        fetch_fn=_make_fetch_fn(pages),
        response_model=FakePage,
        items_field="items",
    )

    items = [item.name for item in paginator]
    assert items == ["a", "b", "c"]


def test_multi_page_iteration():
    pages = [
        {"items": ["a", "b"], "nextToken": "page2"},
        {"items": ["c", "d"], "nextToken": "page3"},
        {"items": ["e"]},
    ]
    paginator = Paginator(
        fetch_fn=_make_fetch_fn(pages),
        response_model=FakePage,
        items_field="items",
    )

    items = [item.name for item in paginator]
    assert items == ["a", "b", "c", "d", "e"]


def test_empty_page():
    pages = [{"items": []}]
    paginator = Paginator(
        fetch_fn=_make_fetch_fn(pages),
        response_model=FakePage,
        items_field="items",
    )

    items = list(paginator)
    assert items == []


def test_stops_at_no_next_token():
    """Paginator should stop when nextToken is absent."""
    call_count = [0]

    def fetch(token: str | None) -> httpx.Response:
        call_count[0] += 1
        if call_count[0] == 1:
            return httpx.Response(200, json={"items": ["a"], "nextToken": "t2"})
        return httpx.Response(200, json={"items": ["b"]})

    paginator = Paginator(
        fetch_fn=fetch,
        response_model=FakePage,
        items_field="items",
    )

    items = [item.name for item in paginator]
    assert items == ["a", "b"]
    assert call_count[0] == 2


def test_first_page():
    pages = [{"items": ["a", "b"], "nextToken": "page2"}]
    paginator = Paginator(
        fetch_fn=_make_fetch_fn(pages),
        response_model=FakePage,
        items_field="items",
    )

    page = paginator.first_page()
    assert len(page.items) == 2
    assert page.next_token == "page2"


def test_none_items_treated_as_empty():
    """If the items field is None, treat it as empty list."""
    pages = [{"items": None}]
    paginator = Paginator(
        fetch_fn=_make_fetch_fn(pages),
        response_model=FakePage,
        items_field="items",
    )

    items = list(paginator)
    assert items == []


# ---------------------------------------------------------------------------
# max_items tests
# ---------------------------------------------------------------------------


def test_max_items_caps_single_page():
    """max_items=2 stops after 2 items even though page has 5."""
    pages = [{"items": ["a", "b", "c", "d", "e"]}]
    paginator = Paginator(
        fetch_fn=_make_fetch_fn(pages),
        response_model=FakePage,
        items_field="items",
        max_items=2,
    )

    items = [item.name for item in paginator]
    assert items == ["a", "b"]


def test_max_items_caps_across_pages():
    """max_items=3 stops mid-way through second page."""
    pages = [
        {"items": ["a", "b"], "nextToken": "page2"},
        {"items": ["c", "d"], "nextToken": "page3"},
        {"items": ["e"]},
    ]
    paginator = Paginator(
        fetch_fn=_make_fetch_fn(pages),
        response_model=FakePage,
        items_field="items",
        max_items=3,
    )

    items = [item.name for item in paginator]
    assert items == ["a", "b", "c"]


def test_max_items_none_returns_all():
    """max_items=None (default) returns everything."""
    pages = [
        {"items": ["a", "b"], "nextToken": "page2"},
        {"items": ["c"]},
    ]
    paginator = Paginator(
        fetch_fn=_make_fetch_fn(pages),
        response_model=FakePage,
        items_field="items",
        max_items=None,
    )

    items = [item.name for item in paginator]
    assert items == ["a", "b", "c"]


def test_max_items_larger_than_total():
    """max_items larger than total items returns everything without error."""
    pages = [{"items": ["a", "b"]}]
    paginator = Paginator(
        fetch_fn=_make_fetch_fn(pages),
        response_model=FakePage,
        items_field="items",
        max_items=100,
    )

    items = [item.name for item in paginator]
    assert items == ["a", "b"]


def test_max_items_zero_returns_nothing():
    """max_items=0 returns no items."""
    call_count = [0]

    def fetch(token):
        call_count[0] += 1
        return httpx.Response(200, json={"items": ["a", "b"]})

    paginator = Paginator(
        fetch_fn=fetch,
        response_model=FakePage,
        items_field="items",
        max_items=0,
    )

    items = list(paginator)
    assert items == []
    # Still fetches the first page (max_items is checked during yield)
    assert call_count[0] == 1


def test_max_items_prevents_unnecessary_fetches():
    """When max_items is satisfied within first page, second page is never fetched."""
    call_count = [0]

    def fetch(token):
        call_count[0] += 1
        if call_count[0] == 1:
            return httpx.Response(200, json={"items": ["a", "b", "c"], "nextToken": "t2"})
        return httpx.Response(200, json={"items": ["d", "e"]})

    paginator = Paginator(
        fetch_fn=fetch,
        response_model=FakePage,
        items_field="items",
        max_items=2,
    )

    items = [item.name for item in paginator]
    assert items == ["a", "b"]
    assert call_count[0] == 1  # Second page never fetched


def test_max_items_set_after_construction():
    """max_items can be set via _max_items after construction (used by Tier 3)."""
    pages = [{"items": ["a", "b", "c", "d", "e"]}]
    paginator = Paginator(
        fetch_fn=_make_fetch_fn(pages),
        response_model=FakePage,
        items_field="items",
    )
    paginator._max_items = 3

    items = [item.name for item in paginator]
    assert items == ["a", "b", "c"]


# ---------------------------------------------------------------------------
# _transform_list_kwargs tests
# ---------------------------------------------------------------------------


def test_transform_page_size_to_max_results():
    kwargs = {"page_size": 25, "sort_by": "name"}
    max_items = _transform_list_kwargs(kwargs)
    assert kwargs == {"max_results": 25, "sort_by": "name"}
    assert max_items is None


def test_transform_extracts_max_items():
    kwargs = {"max_items": 100, "sort_by": "name"}
    max_items = _transform_list_kwargs(kwargs)
    assert kwargs == {"sort_by": "name"}
    assert max_items == 100


def test_transform_suppresses_next_token():
    kwargs = {"next_token": "abc123", "sort_by": "name"}
    max_items = _transform_list_kwargs(kwargs)
    assert kwargs == {"sort_by": "name"}
    assert max_items is None


def test_transform_all_together():
    kwargs = {"page_size": 20, "max_items": 50, "next_token": "tok", "sort_by": "name"}
    max_items = _transform_list_kwargs(kwargs)
    assert kwargs == {"max_results": 20, "sort_by": "name"}
    assert max_items == 50


def test_transform_no_convenience_params():
    kwargs = {"sort_by": "name", "max_results": 30}
    max_items = _transform_list_kwargs(kwargs)
    assert kwargs == {"sort_by": "name", "max_results": 30}
    assert max_items is None
