"""Tests for the Cerebe sync client — headers, errors, retries."""

from __future__ import annotations

import httpx
import pytest

from cerebe import Cerebe
from cerebe._errors import (
    AuthenticationError,
    NotFoundError,
    ServerError,
    ValidationError,
)


def _mock_response(body: dict, status: int = 200, headers: dict | None = None) -> httpx.Response:
    return httpx.Response(status_code=status, json=body, headers=headers or {})


class TestClientInit:
    def test_requires_api_key(self) -> None:
        with pytest.raises(ValueError, match="API key is required"):
            Cerebe(api_key="")

    def test_has_all_resources(self) -> None:
        client = Cerebe(api_key="ck_test", max_retries=0)
        for attr in ("memory", "knowledge", "storage", "meta_learning", "agents", "sessions", "graph"):
            assert hasattr(client, attr), f"Missing resource: {attr}"


class TestHeaders:
    def test_sends_api_key_and_content_type(self, httpx_mock) -> None:
        httpx_mock.add_response(json={"data": "ok", "meta": {}})
        client = Cerebe(api_key="ck_test_key", max_retries=0)
        client.request("GET", "/api/v1/health")

        req = httpx_mock.get_request()
        assert req.headers["x-api-key"] == "ck_test_key"
        assert req.headers["content-type"] == "application/json"
        assert "cerebe-python" in req.headers["user-agent"]

    def test_sends_project_header(self, httpx_mock) -> None:
        httpx_mock.add_response(json={"data": "ok", "meta": {}})
        client = Cerebe(api_key="ck_test", project="proj_1", max_retries=0)
        client.request("GET", "/test")

        req = httpx_mock.get_request()
        assert req.headers["x-cerebe-project"] == "proj_1"


class TestResponseParsing:
    def test_parses_envelope(self, httpx_mock) -> None:
        httpx_mock.add_response(json={
            "data": {"memories": ["a"]},
            "meta": {"request_id": "req_123", "org_id": "org_1"},
        })
        client = Cerebe(api_key="ck_test", max_retries=0)
        result = client.request("GET", "/test")

        assert result.data == {"memories": ["a"]}
        assert result.meta.request_id == "req_123"
        assert result.meta.org_id == "org_1"


class TestErrors:
    def test_401_raises_authentication_error(self, httpx_mock) -> None:
        httpx_mock.add_response(status_code=401, json={"error": {"message": "Invalid key"}})
        client = Cerebe(api_key="ck_test", max_retries=0)
        with pytest.raises(AuthenticationError):
            client.request("GET", "/test")

    def test_404_raises_not_found_error(self, httpx_mock) -> None:
        httpx_mock.add_response(status_code=404, json={"error": {"message": "Not found"}})
        client = Cerebe(api_key="ck_test", max_retries=0)
        with pytest.raises(NotFoundError):
            client.request("GET", "/test")

    def test_422_raises_validation_error(self, httpx_mock) -> None:
        httpx_mock.add_response(status_code=422, json={"error": {"message": "Bad input"}})
        client = Cerebe(api_key="ck_test", max_retries=0)
        with pytest.raises(ValidationError):
            client.request("GET", "/test")

    def test_500_raises_server_error(self, httpx_mock) -> None:
        httpx_mock.add_response(status_code=500, json={"error": {"message": "Internal"}})
        client = Cerebe(api_key="ck_test", max_retries=0)
        with pytest.raises(ServerError):
            client.request("GET", "/test")


class TestRetries:
    def test_retries_on_429(self, httpx_mock) -> None:
        httpx_mock.add_response(
            status_code=429,
            json={"error": {}},
            headers={"Retry-After": "0"},
        )
        httpx_mock.add_response(json={"data": "ok", "meta": {}})

        client = Cerebe(api_key="ck_test", max_retries=1)
        result = client.request("GET", "/test")
        assert result.data == "ok"

    def test_retries_on_5xx(self, httpx_mock) -> None:
        httpx_mock.add_response(status_code=502, json={"error": {}})
        httpx_mock.add_response(json={"data": "recovered", "meta": {}})

        client = Cerebe(api_key="ck_test", max_retries=1)
        result = client.request("GET", "/test")
        assert result.data == "recovered"

    def test_exhausted_retries_raises(self, httpx_mock) -> None:
        httpx_mock.add_response(status_code=500, json={"error": {"message": "down"}})
        httpx_mock.add_response(status_code=500, json={"error": {"message": "down"}})

        client = Cerebe(api_key="ck_test", max_retries=1)
        with pytest.raises(ServerError):
            client.request("GET", "/test")
