import httpx
import pytest

from gaiia import (
    GaiiaAPIError,
    GaiiaClient,
    GaiiaMutationError,
    GaiiaRateLimitedError,
)
from gaiia.client import API_KEY_HEADER, TIMEZONE_HEADER


def _client(handler):
    transport = httpx.MockTransport(handler)
    return GaiiaClient(
        api_key="gaiia_sk_sbox_test",
        timezone="America/Chicago",
        transport=transport,
    )


def test_query_sends_expected_headers_and_body():
    captured = {}

    def handler(request: httpx.Request) -> httpx.Response:
        captured["headers"] = dict(request.headers)
        captured["body"] = request.read()
        return httpx.Response(200, json={"data": {"x": 1}})

    with _client(handler) as g:
        out = g.query("{ x }", {"v": 1})
    assert out == {"x": 1}
    assert captured["headers"][API_KEY_HEADER.lower()] == "gaiia_sk_sbox_test"
    assert captured["headers"][TIMEZONE_HEADER] == "America/Chicago"
    import json
    body = json.loads(captured["body"])
    assert body["query"] == "{ x }"
    assert body["variables"] == {"v": 1}


def test_top_level_errors_raise_api_error():
    def handler(request):
        return httpx.Response(
            200,
            json={"errors": [{"message": "bad", "extensions": {"code": "BAD_USER_INPUT"}}]},
        )

    with _client(handler) as g, pytest.raises(GaiiaAPIError) as exc:
        g.query("{}")
    assert exc.value.errors[0].code == "BAD_USER_INPUT"


def test_rate_limited_classifies_separately():
    def handler(request):
        return httpx.Response(
            200,
            json={
                "errors": [
                    {
                        "message": "too many",
                        "extensions": {
                            "code": "RATE_LIMITED",
                            "limit": 500,
                            "used": 500,
                            "remaining": 0,
                            "retryAt": "2026-06-27T19:01:02.000Z",
                        },
                    }
                ]
            },
            headers={
                "X-Rate-Limit-Allowed": "false",
                "X-Rate-Limit-Limit": "500",
                "X-Rate-Limit-Used": "500",
                "X-Rate-Limit-Remaining": "0",
            },
        )

    with _client(handler) as g, pytest.raises(GaiiaRateLimitedError) as exc:
        g.query("{}")
    assert exc.value.limit == 500
    assert g.last_rate_limit is not None
    assert g.last_rate_limit.remaining == 0


def test_mutate_raises_on_payload_errors():
    def handler(request):
        return httpx.Response(
            200,
            json={
                "data": {
                    "createTicket": {
                        "ticket": None,
                        "errors": [{"code": "TITLE_TOO_LONG", "message": "long"}],
                    }
                }
            },
        )

    with _client(handler) as g, pytest.raises(GaiiaMutationError) as exc:
        g.mutate("mutation { createTicket { ticket { id } errors { code message } } }", "createTicket")
    assert exc.value.errors[0]["code"] == "TITLE_TOO_LONG"


def test_iter_nodes_paginates_via_cursor():
    calls = []

    def handler(request):
        body = request.read().decode()
        calls.append(body)
        if '"after":null' in body.replace(" ", ""):
            return httpx.Response(
                200,
                json={
                    "data": {
                        "things": {
                            "nodes": [{"id": 1}, {"id": 2}],
                            "pageInfo": {"hasNextPage": True, "endCursor": "c1"},
                        }
                    }
                },
            )
        return httpx.Response(
            200,
            json={
                "data": {
                    "things": {
                        "nodes": [{"id": 3}],
                        "pageInfo": {"hasNextPage": False, "endCursor": None},
                    }
                }
            },
        )

    with _client(handler) as g:
        ids = [n["id"] for n in g.iter_nodes(
            "query Q($after: String) { things(first: 2, after: $after) { nodes { id } pageInfo { hasNextPage endCursor } } }",
            "things",
        )]
    assert ids == [1, 2, 3]
    assert len(calls) == 2


def test_missing_api_key_raises():
    import os

    saved = os.environ.pop("GAIIA_KEY", None)
    try:
        with pytest.raises(ValueError):
            GaiiaClient()
    finally:
        if saved is not None:
            os.environ["GAIIA_KEY"] = saved
