"""Tests for shared transport layer."""

import builtins

import httpx
import pytest
import respx

from primitif._exceptions import (
    AuthError,
    NetworkError,
    PrimitifError,
    RateLimitError,
    ServerError,
)
from primitif._transport import request_with_retries


_EXC_MAP = {401: AuthError, 429: RateLimitError}


def _make_client(base_url: str) -> httpx.Client:
    return httpx.Client(base_url=base_url, headers={"Authorization": "Bearer test"})


@respx.mock
def test_successful_request():
    respx.get("https://api.test.com/v1/resource").mock(
        return_value=httpx.Response(200, json={"id": "123"})
    )
    client = _make_client("https://api.test.com")
    result = request_with_retries(
        client, "GET", "/v1/resource", 2,
        exc_map=_EXC_MAP, server_exc=ServerError, base_exc=PrimitifError,
    )
    assert result == {"id": "123"}
    client.close()


@respx.mock
def test_204_returns_empty_dict():
    respx.delete("https://api.test.com/v1/resource").mock(
        return_value=httpx.Response(204)
    )
    client = _make_client("https://api.test.com")
    result = request_with_retries(
        client, "DELETE", "/v1/resource", 0,
        exc_map=_EXC_MAP, server_exc=ServerError, base_exc=PrimitifError,
    )
    assert result == {}
    client.close()


@respx.mock
def test_401_raises_auth_error():
    respx.get("https://api.test.com/v1/resource").mock(
        return_value=httpx.Response(401, json={"error": "unauthorized", "message": "Bad token"})
    )
    client = _make_client("https://api.test.com")
    with pytest.raises(AuthError) as exc_info:
        request_with_retries(
            client, "GET", "/v1/resource", 0,
            exc_map=_EXC_MAP, server_exc=ServerError, base_exc=PrimitifError,
        )
    assert exc_info.value.status_code == 401
    client.close()


@respx.mock
def test_500_raises_server_error():
    respx.get("https://api.test.com/v1/resource").mock(
        return_value=httpx.Response(500, json={"error": "internal", "message": "Oops"})
    )
    client = _make_client("https://api.test.com")
    with pytest.raises(ServerError):
        request_with_retries(
            client, "GET", "/v1/resource", 0,
            exc_map=_EXC_MAP, server_exc=ServerError, base_exc=PrimitifError,
        )
    client.close()


@respx.mock
def test_retries_on_500():
    route = respx.get("https://api.test.com/v1/resource")
    route.side_effect = [
        httpx.Response(500, json={"error": "internal", "message": "Oops"}),
        httpx.Response(200, json={"ok": True}),
    ]
    client = _make_client("https://api.test.com")
    result = request_with_retries(
        client, "GET", "/v1/resource", 1,
        exc_map=_EXC_MAP, server_exc=ServerError, base_exc=PrimitifError,
    )
    assert result == {"ok": True}
    assert route.call_count == 2
    client.close()


@respx.mock
def test_timeout_raises_connection_error():
    respx.get("https://api.test.com/v1/resource").mock(
        side_effect=httpx.ReadTimeout("timed out")
    )
    client = _make_client("https://api.test.com")
    with pytest.raises(NetworkError, match="ReadTimeout"):
        request_with_retries(
            client, "GET", "/v1/resource", 0,
            exc_map=_EXC_MAP, server_exc=ServerError, base_exc=PrimitifError,
        )
    client.close()


@respx.mock
def test_connect_error_retried():
    route = respx.get("https://api.test.com/v1/resource")
    route.side_effect = [
        httpx.ConnectError("connection refused"),
        httpx.Response(200, json={"ok": True}),
    ]
    client = _make_client("https://api.test.com")
    result = request_with_retries(
        client, "GET", "/v1/resource", 1,
        exc_map=_EXC_MAP, server_exc=ServerError, base_exc=PrimitifError,
    )
    assert result == {"ok": True}
    assert route.call_count == 2
    client.close()


@respx.mock
def test_non_json_200_raises():
    respx.get("https://api.test.com/v1/resource").mock(
        return_value=httpx.Response(200, content=b"<html>Bad Gateway</html>",
                                    headers={"content-type": "text/html"})
    )
    client = _make_client("https://api.test.com")
    with pytest.raises(PrimitifError, match="Expected JSON"):
        request_with_retries(
            client, "GET", "/v1/resource", 0,
            exc_map=_EXC_MAP, server_exc=ServerError, base_exc=PrimitifError,
        )
    client.close()


@respx.mock
def test_422_list_detail_formatted():
    """FastAPI-style 422 validation errors are formatted readably."""
    respx.post("https://api.test.com/v1/resource").mock(
        return_value=httpx.Response(422, json={
            "detail": [
                {"loc": ["body", "to"], "msg": "field required", "type": "value_error.missing"},
                {"loc": ["body", "subject"], "msg": "field required", "type": "value_error.missing"},
            ]
        })
    )
    client = _make_client("https://api.test.com")
    with pytest.raises(PrimitifError) as exc_info:
        request_with_retries(
            client, "POST", "/v1/resource", 0,
            exc_map=_EXC_MAP, server_exc=ServerError, base_exc=PrimitifError,
        )
    assert "body -> to: field required" in exc_info.value.message
    assert "body -> subject: field required" in exc_info.value.message
    client.close()


def test_network_error_does_not_shadow_builtin():
    """NetworkError is distinct from Python's built-in ConnectionError."""
    from primitif._exceptions import NetworkError
    assert NetworkError is not builtins.ConnectionError
    err = NetworkError(message="timeout")
    assert not isinstance(err, builtins.ConnectionError)
