"""Tests for the main ConcentriqClient class."""

import httpx
import pytest
from pytest_httpx import HTTPXMock

from concentriq import ConcentriqClient
from concentriq.auth import ApiKeyAuth, BasicAuth
from concentriq.exceptions import (
    APIError,
    AuthenticationError,
    AuthorizationError,
    NotFoundError,
    ServerError,
    ValidationError,
)


class TestConcentriqClientInit:
    """Tests for ConcentriqClient initialization."""

    def test_creates_with_base_url_and_auth(self):
        """ConcentriqClient should accept base_url and auth."""
        auth = ApiKeyAuth(api_key="test-key")
        client = ConcentriqClient(base_url="https://test.concentriq.com", auth=auth)

        assert client.base_url == "https://test.concentriq.com"
        assert client.auth == auth

    def test_strips_trailing_slash_from_base_url(self):
        """ConcentriqClient should strip trailing slash from base_url."""
        auth = ApiKeyAuth(api_key="test-key")
        client = ConcentriqClient(base_url="https://test.concentriq.com/", auth=auth)

        assert client.base_url == "https://test.concentriq.com"

    def test_requires_base_url(self):
        """ConcentriqClient should require base_url."""
        auth = ApiKeyAuth(api_key="test-key")
        with pytest.raises(ValueError, match="base_url is required"):
            ConcentriqClient(base_url="", auth=auth)

    def test_requires_auth(self):
        """ConcentriqClient should require auth."""
        with pytest.raises(ValueError, match="auth is required"):
            ConcentriqClient(base_url="https://test.concentriq.com", auth=None)

    def test_creates_httpx_client(self):
        """ConcentriqClient should create an httpx Client."""
        auth = ApiKeyAuth(api_key="test-key")
        client = ConcentriqClient(base_url="https://test.concentriq.com", auth=auth)

        assert isinstance(client._client, httpx.Client)

    def test_custom_timeout(self):
        """ConcentriqClient should accept custom timeout."""
        auth = ApiKeyAuth(api_key="test-key")
        client = ConcentriqClient(
            base_url="https://test.concentriq.com",
            auth=auth,
            timeout=60.0
        )

        assert client.timeout == 60.0


class TestConcentriqClientRequest:
    """Tests for ConcentriqClient._request method."""

    def test_adds_auth_headers_to_request(self, httpx_mock: HTTPXMock):
        """Client should add authentication headers to requests."""
        httpx_mock.add_response(json={"data": "test"})

        auth = BasicAuth(username="user", password="pass")
        client = ConcentriqClient(base_url="https://test.concentriq.com", auth=auth)

        client._request("GET", "/api/test")

        request = httpx_mock.get_request()
        assert "Authorization" in request.headers
        assert request.headers["Authorization"].startswith("Basic ")

    def test_makes_get_request(self, httpx_mock: HTTPXMock):
        """Client should make GET requests."""
        httpx_mock.add_response(json={"data": "test"})

        auth = ApiKeyAuth(api_key="test-key")
        client = ConcentriqClient(base_url="https://test.concentriq.com", auth=auth)

        response = client._request("GET", "/api/test")

        request = httpx_mock.get_request()
        assert request.method == "GET"
        assert str(request.url) == "https://test.concentriq.com/api/test"
        assert response.status_code == 200

    def test_makes_post_request_with_json(self, httpx_mock: HTTPXMock):
        """Client should make POST requests with JSON body."""
        httpx_mock.add_response(json={"id": 123}, status_code=201)

        auth = ApiKeyAuth(api_key="test-key")
        client = ConcentriqClient(base_url="https://test.concentriq.com", auth=auth)

        data = {"name": "Test"}
        response = client._request("POST", "/api/test", json=data)

        request = httpx_mock.get_request()
        assert request.method == "POST"
        assert response.status_code == 201

    def test_handles_query_params(self, httpx_mock: HTTPXMock):
        """Client should handle query parameters."""
        httpx_mock.add_response(json={"results": []})

        auth = ApiKeyAuth(api_key="test-key")
        client = ConcentriqClient(base_url="https://test.concentriq.com", auth=auth)

        client._request("GET", "/api/test", params={"page": 1, "limit": 10})

        request = httpx_mock.get_request()
        assert "page=1" in str(request.url)
        assert "limit=10" in str(request.url)


class TestConcentriqClientErrorHandling:
    """Tests for error handling in ConcentriqClient."""

    def test_raises_authentication_error_on_401(self, httpx_mock: HTTPXMock):
        """Client should raise AuthenticationError on 401."""
        httpx_mock.add_response(status_code=401, json={"error": "Unauthorized"})

        auth = ApiKeyAuth(api_key="test-key")
        client = ConcentriqClient(base_url="https://test.concentriq.com", auth=auth)

        with pytest.raises(AuthenticationError, match="401"):
            client._request("GET", "/api/test")

    def test_raises_authorization_error_on_403(self, httpx_mock: HTTPXMock):
        """Client should raise AuthorizationError on 403."""
        httpx_mock.add_response(status_code=403, json={"error": "Forbidden"})

        auth = ApiKeyAuth(api_key="test-key")
        client = ConcentriqClient(base_url="https://test.concentriq.com", auth=auth)

        with pytest.raises(AuthorizationError, match="403"):
            client._request("GET", "/api/test")

    def test_raises_not_found_error_on_404(self, httpx_mock: HTTPXMock):
        """Client should raise NotFoundError on 404."""
        httpx_mock.add_response(status_code=404, json={"error": "Not found"})

        auth = ApiKeyAuth(api_key="test-key")
        client = ConcentriqClient(base_url="https://test.concentriq.com", auth=auth)

        with pytest.raises(NotFoundError, match="404"):
            client._request("GET", "/api/test/123")

    def test_raises_validation_error_on_400(self, httpx_mock: HTTPXMock):
        """Client should raise ValidationError on 400."""
        httpx_mock.add_response(status_code=400, json={"error": "Bad request"})

        auth = ApiKeyAuth(api_key="test-key")
        client = ConcentriqClient(base_url="https://test.concentriq.com", auth=auth)

        with pytest.raises(ValidationError, match="400"):
            client._request("POST", "/api/test", json={})

    def test_raises_validation_error_on_422(self, httpx_mock: HTTPXMock):
        """Client should raise ValidationError on 422."""
        httpx_mock.add_response(
            status_code=422,
            json={"error": "Validation failed", "fields": {"name": "required"}}
        )

        auth = ApiKeyAuth(api_key="test-key")
        client = ConcentriqClient(base_url="https://test.concentriq.com", auth=auth)

        with pytest.raises(ValidationError, match="422"):
            client._request("POST", "/api/test", json={})

    def test_raises_server_error_on_500(self, httpx_mock: HTTPXMock):
        """Client should raise ServerError on 500."""
        httpx_mock.add_response(status_code=500, json={"error": "Internal error"})

        auth = ApiKeyAuth(api_key="test-key")
        client = ConcentriqClient(base_url="https://test.concentriq.com", auth=auth)

        with pytest.raises(ServerError, match="500"):
            client._request("GET", "/api/test")

    def test_raises_server_error_on_503(self, httpx_mock: HTTPXMock):
        """Client should raise ServerError on 503."""
        httpx_mock.add_response(status_code=503, json={"error": "Service unavailable"})

        auth = ApiKeyAuth(api_key="test-key")
        client = ConcentriqClient(base_url="https://test.concentriq.com", auth=auth)

        with pytest.raises(ServerError, match="503"):
            client._request("GET", "/api/test")

    def test_raises_api_error_on_other_4xx(self, httpx_mock: HTTPXMock):
        """Client should raise APIError on other 4xx codes."""
        httpx_mock.add_response(status_code=429, json={"error": "Too many requests"})

        auth = ApiKeyAuth(api_key="test-key")
        client = ConcentriqClient(base_url="https://test.concentriq.com", auth=auth)

        with pytest.raises(APIError, match="429"):
            client._request("GET", "/api/test")


class TestConcentriqClientContextManager:
    """Tests for ConcentriqClient context manager."""

    def test_supports_context_manager(self):
        """ConcentriqClient should support context manager protocol."""
        auth = ApiKeyAuth(api_key="test-key")

        with ConcentriqClient(base_url="https://test.concentriq.com", auth=auth) as client:
            assert isinstance(client, ConcentriqClient)

    def test_closes_httpx_client_on_exit(self):
        """ConcentriqClient should close httpx client on context exit."""
        auth = ApiKeyAuth(api_key="test-key")

        with ConcentriqClient(base_url="https://test.concentriq.com", auth=auth) as client:
            httpx_client = client._client
            assert not httpx_client.is_closed

        assert httpx_client.is_closed
