from httpx import AsyncClient


async def gql(client: AsyncClient, query: str) -> dict:
    resp = await client.post("/graphql", json={"query": query})
    return resp.json()


async def test_user_posts_batched_single_sql(client: AsyncClient, query_counter: dict[str, int]):
    result = await gql(
        client,
        """
        { users(first: 3) {
            edges { node { posts(first: 5) {
                edges { node { id title } }
            } } }
        } }
        """,
    )
    assert "errors" not in result
    # 1 query for users + 1 batched query for all posts
    assert query_counter["n"] == 2

    users_edges = result["data"]["users"]["edges"]
    assert len(users_edges) == 3
    for user_edge in users_edges:
        assert len(user_edge["node"]["posts"]["edges"]) == 3


async def test_user_posts_has_next_page(client: AsyncClient, query_counter: dict[str, int]):
    result = await gql(
        client,
        """
        { users(first: 3) {
            edges { node { posts(first: 2) {
                edges { node { id } }
                pageInfo { hasNextPage }
            } } }
        } }
        """,
    )
    assert "errors" not in result
    assert query_counter["n"] == 2

    for user_edge in result["data"]["users"]["edges"]:
        posts = user_edge["node"]["posts"]
        assert len(posts["edges"]) == 2
        assert posts["pageInfo"]["hasNextPage"] is True


async def test_user_posts_total_count_still_one_query(client: AsyncClient, query_counter: dict[str, int]):
    result = await gql(
        client,
        """
        { users(first: 3) {
            edges { node { posts(first: 5) {
                edges { node { id } }
                pageInfo { totalCount }
            } } }
        } }
        """,
    )
    assert "errors" not in result
    # Still only 2 queries: 1 for users + 1 batched posts (totalCount via window fn)
    assert query_counter["n"] == 2

    for user_edge in result["data"]["users"]["edges"]:
        assert user_edge["node"]["posts"]["pageInfo"]["totalCount"] == 3


async def test_user_posts_last(client: AsyncClient, query_counter: dict[str, int]):
    result = await gql(
        client,
        """
        { users(first: 3) {
            edges { node { posts(last: 2) {
                edges { node { id } }
                pageInfo { hasPreviousPage }
            } } }
        } }
        """,
    )
    assert "errors" not in result
    assert query_counter["n"] == 2

    for user_edge in result["data"]["users"]["edges"]:
        posts = user_edge["node"]["posts"]
        assert len(posts["edges"]) == 2
        assert posts["pageInfo"]["hasPreviousPage"] is True
        # edges should be in ascending order
        ids = [e["node"]["id"] for e in posts["edges"]]
        assert ids == sorted(ids)


async def test_user_posts_correct_assignment(client: AsyncClient):
    result = await gql(
        client,
        """
        { users(first: 3) {
            edges { node { id posts(first: 5) {
                edges { node { id } }
            } } }
        } }
        """,
    )
    assert "errors" not in result

    for user_edge in result["data"]["users"]["edges"]:
        user_id = user_edge["node"]["id"]
        post_ids = [e["node"]["id"] for e in user_edge["node"]["posts"]["edges"]]
        # user N owns posts (3N-2), (3N-1), 3N
        expected = [(user_id - 1) * 3 + offset for offset in range(1, 4)]
        assert post_ids == expected
