"""Tests for error handling and HTTP status → exception mapping."""

import httpx
import pytest

from goodmem.errors import (
    APIError,
    AuthenticationError,
    ConflictError,
    NotFoundError,
    PermissionError,
    RateLimitError,
    ServerError,
    ValidationError,
    raise_for_status,
)


def _make_response(status_code: int, body: str = "error body") -> httpx.Response:
    return httpx.Response(status_code=status_code, text=body)


def test_200_no_error():
    raise_for_status(_make_response(200))


def test_201_no_error():
    raise_for_status(_make_response(201))


def test_204_no_error():
    raise_for_status(_make_response(204))


def test_401_raises_authentication_error():
    with pytest.raises(AuthenticationError) as exc_info:
        raise_for_status(_make_response(401))
    assert exc_info.value.status_code == 401
    assert "error body" in exc_info.value.body


def test_403_raises_permission_error():
    with pytest.raises(PermissionError) as exc_info:
        raise_for_status(_make_response(403))
    assert exc_info.value.status_code == 403


def test_404_raises_not_found_error():
    with pytest.raises(NotFoundError) as exc_info:
        raise_for_status(_make_response(404))
    assert exc_info.value.status_code == 404


def test_409_raises_conflict_error():
    with pytest.raises(ConflictError) as exc_info:
        raise_for_status(_make_response(409))
    assert exc_info.value.status_code == 409


def test_422_raises_validation_error():
    with pytest.raises(ValidationError) as exc_info:
        raise_for_status(_make_response(422))
    assert exc_info.value.status_code == 422


def test_429_raises_rate_limit_error():
    with pytest.raises(RateLimitError) as exc_info:
        raise_for_status(_make_response(429))
    assert exc_info.value.status_code == 429


def test_500_raises_server_error():
    with pytest.raises(ServerError) as exc_info:
        raise_for_status(_make_response(500))
    assert exc_info.value.status_code == 500


def test_502_raises_server_error():
    with pytest.raises(ServerError) as exc_info:
        raise_for_status(_make_response(502))
    assert exc_info.value.status_code == 502


def test_418_raises_generic_api_error():
    """Unknown 4xx maps to base APIError."""
    with pytest.raises(APIError) as exc_info:
        raise_for_status(_make_response(418, "I'm a teapot"))
    assert exc_info.value.status_code == 418
    assert "I'm a teapot" in exc_info.value.body


def test_error_body_preserved():
    with pytest.raises(APIError) as exc_info:
        raise_for_status(_make_response(400, '{"detail": "bad request"}'))
    assert exc_info.value.body == '{"detail": "bad request"}'


def test_error_message_includes_status():
    with pytest.raises(APIError) as exc_info:
        raise_for_status(_make_response(400, "bad"))
    assert "400" in str(exc_info.value)


def test_error_body_truncated_in_message():
    """Long bodies are truncated to 500 chars in the message."""
    long_body = "x" * 1000
    with pytest.raises(APIError) as exc_info:
        raise_for_status(_make_response(400, long_body))
    # The full body is preserved in .body
    assert len(exc_info.value.body) == 1000
    # But the message is truncated
    assert len(str(exc_info.value)) <= 510  # "HTTP 400: " + 500
