"""Tests for the HTTP client: retry logic, error parsing, pagination."""

from __future__ import annotations

import httpx
import pytest

from appliku._client import AplikuClient
from appliku._exceptions import (
    AuthenticationError,
    NotFoundError,
    RateLimitError,
    ServerError,
    ValidationError,
    parse_error_response,
)


class TestParseErrorResponse:
    def test_detail_message(self) -> None:
        exc = parse_error_response(404, {"detail": "Not found."})
        assert isinstance(exc, NotFoundError)
        assert "Not found" in str(exc)

    def test_non_field_errors(self) -> None:
        exc = parse_error_response(400, {"non_field_errors": ["Something went wrong"]})
        assert isinstance(exc, ValidationError)
        assert "Something went wrong" in str(exc)

    def test_field_errors(self) -> None:
        exc = parse_error_response(400, {"name": ["This field is required."]})
        assert isinstance(exc, ValidationError)
        assert "name" in str(exc)

    def test_401_raises_auth_error(self) -> None:
        exc = parse_error_response(401, {"detail": "Invalid token."})
        assert isinstance(exc, AuthenticationError)

    def test_429_raises_rate_limit(self) -> None:
        exc = parse_error_response(429, {"detail": "Rate limit exceeded."})
        assert isinstance(exc, RateLimitError)

    def test_500_raises_server_error(self) -> None:
        exc = parse_error_response(500, {"detail": "Internal server error."})
        assert isinstance(exc, ServerError)

    def test_status_code_stored(self) -> None:
        exc = parse_error_response(404, {"detail": "nope"})
        assert exc.status_code == 404


class TestAplikuClient:
    def _make_client(self, responses: list[httpx.Response]) -> AplikuClient:
        def handler(request: httpx.Request) -> httpx.Response:
            return responses.pop(0)

        transport = httpx.MockTransport(handler)
        client = AplikuClient(token="test-token")  # noqa: S106
        client._session = httpx.Client(transport=transport, base_url="https://api.appliku.com")
        return client

    def test_get_success(self) -> None:
        client = self._make_client([httpx.Response(200, json={"id": 1})])
        result = client.get("/api/test/")
        assert result == {"id": 1}

    def test_post_success(self) -> None:
        client = self._make_client([httpx.Response(201, json={"id": 2})])
        result = client.post("/api/test/", json={"name": "test"})
        assert result == {"id": 2}

    def test_delete_success(self) -> None:
        client = self._make_client([httpx.Response(204)])
        client.delete("/api/test/1/")  # should not raise

    def test_404_raises_not_found(self) -> None:
        client = self._make_client([httpx.Response(404, json={"detail": "Not found."})])
        with pytest.raises(NotFoundError):
            client.get("/api/missing/")

    def test_rate_limit_retries(self) -> None:
        client = self._make_client(
            [
                httpx.Response(429, json={"detail": "Rate limited"}, headers={"retry-after": "0"}),
                httpx.Response(200, json={"ok": True}),
            ]
        )
        result = client.get("/api/test/")
        assert result == {"ok": True}

    def test_rate_limit_exhausted_raises(self) -> None:
        client = self._make_client(
            [
                httpx.Response(429, json={"detail": "Rate limited"}, headers={"retry-after": "0"}),
                httpx.Response(429, json={"detail": "Rate limited"}, headers={"retry-after": "0"}),
                httpx.Response(429, json={"detail": "Rate limited"}, headers={"retry-after": "0"}),
            ]
        )
        with pytest.raises(RateLimitError):
            client.get("/api/test/")

    def test_server_error_retries(self) -> None:
        client = self._make_client(
            [
                httpx.Response(500, json={"detail": "Server error"}),
                httpx.Response(200, json={"recovered": True}),
            ]
        )
        result = client.get("/api/test/")
        assert result == {"recovered": True}

    def test_server_error_exhausted_raises(self) -> None:
        client = self._make_client(
            [
                httpx.Response(500, json={"detail": "Server error"}),
                httpx.Response(500, json={"detail": "Server error"}),
                httpx.Response(500, json={"detail": "Server error"}),
            ]
        )
        with pytest.raises(ServerError):
            client.get("/api/test/")

    def test_paginate_returns_models(self) -> None:
        from appliku._models import Team

        client = self._make_client(
            [
                httpx.Response(
                    200,
                    json={
                        "count": 1,
                        "next": None,
                        "previous": None,
                        "results": [{"id": 1, "team_path": "test", "name": "Test"}],
                    },
                )
            ]
        )
        result = client.paginate("/api/team", response_type=Team)
        assert result.count == 1
        assert isinstance(result.results[0], Team)
        assert result.results[0].name == "Test"

    def test_paginate_all_follows_next(self) -> None:
        from appliku._models import Team

        page1 = httpx.Response(
            200,
            json={
                "count": 2,
                "next": "https://api.appliku.com/api/team?page=2",
                "previous": None,
                "results": [{"id": 1, "team_path": "a", "name": "A"}],
            },
        )
        page2 = httpx.Response(
            200,
            json={
                "count": 2,
                "next": None,
                "previous": "https://api.appliku.com/api/team?page=1",
                "results": [{"id": 2, "team_path": "b", "name": "B"}],
            },
        )
        client = self._make_client([page1, page2])
        items = list(client.paginate_all("/api/team", response_type=Team))
        assert len(items) == 2
        assert items[0].name == "A"
        assert items[1].name == "B"
