"""Tests for Crustdata API client."""

import pytest
import respx
from httpx import Response

from crustdata_mcp.client import (
    CrustdataAPIError,
    CrustdataAuthError,
    CrustdataClient,
    CrustdataNotFoundError,
    CrustdataRateLimitError,
    CrustdataServerError,
)


@pytest.fixture
def api_token():
    """Test API token."""
    return "test-api-token"


@pytest.fixture
def base_url():
    """Test base URL."""
    return "https://api.crustdata.com"


class TestCrustdataClient:
    """Tests for CrustdataClient class."""

    @pytest.mark.asyncio
    @respx.mock
    async def test_get_success(self, api_token, base_url):
        """Test successful GET request."""
        expected_data = {"credits": 1000, "tier": "professional"}
        respx.get(f"{base_url}/user/credits").mock(
            return_value=Response(200, json=expected_data)
        )

        async with CrustdataClient(api_token, base_url) as client:
            result = await client.get("/user/credits")
            assert result == expected_data

    @pytest.mark.asyncio
    @respx.mock
    async def test_get_with_params(self, api_token, base_url):
        """Test GET request with query parameters."""
        expected_data = {"results": []}
        respx.get(f"{base_url}/screener/company").mock(
            return_value=Response(200, json=expected_data)
        )

        async with CrustdataClient(api_token, base_url) as client:
            result = await client.get("/screener/company", params={"company_domain": "example.com"})
            assert result == expected_data

    @pytest.mark.asyncio
    @respx.mock
    async def test_post_success(self, api_token, base_url):
        """Test successful POST request."""
        request_data = {"company_name": "Acme Corp"}
        expected_data = {"company_id": 12345, "company_name": "Acme Corp"}
        respx.post(f"{base_url}/screener/identify/").mock(
            return_value=Response(200, json=expected_data)
        )

        async with CrustdataClient(api_token, base_url) as client:
            result = await client.post("/screener/identify/", data=request_data)
            assert result == expected_data

    @pytest.mark.asyncio
    @respx.mock
    async def test_auth_error(self, api_token, base_url):
        """Test 401 authentication error."""
        respx.get(f"{base_url}/user/credits").mock(
            return_value=Response(401, json={"error": "Invalid token"})
        )

        async with CrustdataClient(api_token, base_url) as client:
            with pytest.raises(CrustdataAuthError) as exc_info:
                await client.get("/user/credits")
            assert "Invalid token" in str(exc_info.value)
            assert exc_info.value.status_code == 401

    @pytest.mark.asyncio
    @respx.mock
    async def test_not_found_error(self, api_token, base_url):
        """Test 404 not found error."""
        respx.get(f"{base_url}/screener/company").mock(
            return_value=Response(404, json={"error": "Company not found"})
        )

        async with CrustdataClient(api_token, base_url) as client:
            with pytest.raises(CrustdataNotFoundError) as exc_info:
                await client.get("/screener/company")
            assert "Company not found" in str(exc_info.value)
            assert exc_info.value.status_code == 404

    @pytest.mark.asyncio
    @respx.mock
    async def test_rate_limit_error(self, api_token, base_url):
        """Test 429 rate limit error."""
        respx.get(f"{base_url}/user/credits").mock(
            return_value=Response(429, json={"error": "Rate limit exceeded"})
        )

        async with CrustdataClient(api_token, base_url) as client:
            with pytest.raises(CrustdataRateLimitError) as exc_info:
                await client.get("/user/credits")
            assert "Rate limit exceeded" in str(exc_info.value)
            assert exc_info.value.status_code == 429

    @pytest.mark.asyncio
    @respx.mock
    async def test_server_error(self, api_token, base_url):
        """Test 500 server error."""
        respx.get(f"{base_url}/user/credits").mock(
            return_value=Response(500, json={"error": "Internal server error"})
        )

        async with CrustdataClient(api_token, base_url) as client:
            with pytest.raises(CrustdataServerError) as exc_info:
                await client.get("/user/credits")
            assert "Internal server error" in str(exc_info.value)
            assert exc_info.value.status_code == 500

    @pytest.mark.asyncio
    @respx.mock
    async def test_bad_request_error(self, api_token, base_url):
        """Test 400 bad request error."""
        respx.post(f"{base_url}/screener/identify/").mock(
            return_value=Response(400, json={"detail": "Invalid parameters"})
        )

        async with CrustdataClient(api_token, base_url) as client:
            with pytest.raises(CrustdataAPIError) as exc_info:
                await client.post("/screener/identify/", data={})
            assert "Invalid parameters" in str(exc_info.value)
            assert exc_info.value.status_code == 400

    @pytest.mark.asyncio
    @respx.mock
    async def test_authorization_header(self, api_token, base_url):
        """Test that authorization header is set correctly."""
        route = respx.get(f"{base_url}/user/credits").mock(
            return_value=Response(200, json={"credits": 100})
        )

        async with CrustdataClient(api_token, base_url) as client:
            await client.get("/user/credits")

        assert route.called
        request = route.calls.last.request
        assert request.headers["Authorization"] == f"Token {api_token}"

    @pytest.mark.asyncio
    @respx.mock
    async def test_error_message_from_detail(self, api_token, base_url):
        """Test error message extraction from 'detail' field."""
        respx.get(f"{base_url}/test").mock(
            return_value=Response(400, json={"detail": "Specific error detail"})
        )

        async with CrustdataClient(api_token, base_url) as client:
            with pytest.raises(CrustdataAPIError) as exc_info:
                await client.get("/test")
            assert "Specific error detail" in str(exc_info.value)

    @pytest.mark.asyncio
    @respx.mock
    async def test_error_message_from_message(self, api_token, base_url):
        """Test error message extraction from 'message' field."""
        respx.get(f"{base_url}/test").mock(
            return_value=Response(400, json={"message": "Error message"})
        )

        async with CrustdataClient(api_token, base_url) as client:
            with pytest.raises(CrustdataAPIError) as exc_info:
                await client.get("/test")
            assert "Error message" in str(exc_info.value)

    @pytest.mark.asyncio
    @respx.mock
    async def test_error_message_fallback(self, api_token, base_url):
        """Test error message fallback for unknown response format."""
        respx.get(f"{base_url}/test").mock(
            return_value=Response(400, content=b"")
        )

        async with CrustdataClient(api_token, base_url) as client:
            with pytest.raises(CrustdataAPIError) as exc_info:
                await client.get("/test")
            assert "API error (status 400)" in str(exc_info.value)

    def test_client_initialization(self, api_token):
        """Test client initialization with default values."""
        client = CrustdataClient(api_token)
        assert client.api_token == api_token
        assert client.base_url == "https://api.crustdata.com"
        assert client.timeout == 30.0

    def test_client_initialization_with_overrides(self, api_token):
        """Test client initialization with custom values."""
        client = CrustdataClient(
            api_token,
            base_url="https://custom.api.com/",
            timeout=60.0
        )
        assert client.api_token == api_token
        assert client.base_url == "https://custom.api.com"  # trailing slash stripped
        assert client.timeout == 60.0
