"""
Test suite for connector exceptions
"""

import pytest
from connectors.exceptions import (
    ConnectorError, AuthenticationError, ConfigurationError,
    NetworkError, RateLimitError, APIError, ValidationError,
    TimeoutError, ResourceNotFoundError, PermissionError,
    QuotaExceededError, ServiceUnavailableError,
    DataProcessingError, CacheError, RetryExhaustedError,
    handle_api_error
)


class TestConnectorError:
    """Test base ConnectorError class"""
    
    def test_basic_error_creation(self):
        """Test basic error creation"""
        error = ConnectorError("Test message")
        assert str(error) == "Test message"
        assert error.message == "Test message"
        assert error.error_code is None
        assert error.details == {}
        assert error.original_exception is None
    
    def test_error_with_code_and_details(self):
        """Test error creation with code and details"""
        error = ConnectorError(
            "Test message",
            error_code="TEST_001",
            details={"key": "value"},
            original_exception=ValueError("Original")
        )
        
        expected_str = "Test message (Code: TEST_001) | Details: {'key': 'value'}"
        assert str(error) == expected_str
        assert error.error_code == "TEST_001"
        assert error.details == {"key": "value"}
        assert isinstance(error.original_exception, ValueError)
    
    def test_error_logging(self, caplog):
        """Test error logging functionality"""
        error = ConnectorError("Test message", error_code="TEST_001")
        error.log_error()
        
        assert "Test message (Code: TEST_001)" in caplog.text


class TestSpecificErrors:
    """Test specific error types"""
    
    def test_authentication_error(self):
        """Test AuthenticationError"""
        error = AuthenticationError("Auth failed")
        assert isinstance(error, ConnectorError)
        assert "Auth failed" in str(error)
    
    def test_rate_limit_error(self):
        """Test RateLimitError"""
        error = RateLimitError("Rate limited", retry_after=60)
        assert error.retry_after == 60
        assert isinstance(error, ConnectorError)
    
    def test_api_error(self):
        """Test APIError"""
        error = APIError("API failed", status_code=404, response_data={"error": "Not found"})
        assert error.status_code == 404
        assert error.response_data == {"error": "Not found"}
        assert isinstance(error, ConnectorError)
    
    def test_retry_exhausted_error(self):
        """Test RetryExhaustedError"""
        original_error = ValueError("Original")
        error = RetryExhaustedError("Retries exhausted", attempts=5, last_error=original_error)
        assert error.attempts == 5
        assert error.original_exception == original_error
        assert isinstance(error, ConnectorError)


class MockResponse:
    """Mock HTTP response for testing"""
    
    def __init__(self, status_code, content=None, headers=None):
        self.status_code = status_code
        self.content = content or b'{"error": "test"}'
        self.headers = headers or {}
    
    def json(self):
        import json
        return json.loads(self.content.decode())


class TestAPIErrorHandling:
    """Test API error handling function"""
    
    def test_authentication_error_handling(self):
        """Test 401 error handling"""
        response = MockResponse(401, b'{"error": "Unauthorized"}')
        
        with pytest.raises(AuthenticationError) as exc_info:
            handle_api_error(response, "test operation")
        
        error = exc_info.value
        assert error.status_code == 401
        assert error.error_code == "AUTH_401"
        assert "Authentication failed for test operation" in str(error)
    
    def test_permission_error_handling(self):
        """Test 403 error handling"""
        response = MockResponse(403, b'{"error": "Forbidden"}')
        
        with pytest.raises(PermissionError) as exc_info:
            handle_api_error(response, "test operation")
        
        error = exc_info.value
        assert error.status_code == 403
        assert error.error_code == "PERM_403"
        assert "Permission denied for test operation" in str(error)
    
    def test_not_found_error_handling(self):
        """Test 404 error handling"""
        response = MockResponse(404, b'{"error": "Not found"}')
        
        with pytest.raises(ResourceNotFoundError) as exc_info:
            handle_api_error(response, "test operation")
        
        error = exc_info.value
        assert error.status_code == 404
        assert error.error_code == "NOTFOUND_404"
        assert "Resource not found for test operation" in str(error)
    
    def test_rate_limit_error_handling(self):
        """Test 429 error handling"""
        response = MockResponse(429, b'{"error": "Too many requests"}', {"Retry-After": "120"})
        
        with pytest.raises(RateLimitError) as exc_info:
            handle_api_error(response, "test operation")
        
        error = exc_info.value
        assert error.status_code == 429
        assert error.error_code == "RATE_429"
        assert error.retry_after == 120
        assert "Rate limit exceeded for test operation" in str(error)
    
    def test_service_unavailable_error_handling(self):
        """Test 500 error handling"""
        response = MockResponse(500, b'{"error": "Internal server error"}')
        
        with pytest.raises(ServiceUnavailableError) as exc_info:
            handle_api_error(response, "test operation")
        
        error = exc_info.value
        assert error.status_code == 500
        assert error.error_code == "SERVICE_5XX"
        assert "Service unavailable for test operation" in str(error)
    
    def test_generic_api_error_handling(self):
        """Test generic API error handling"""
        response = MockResponse(422, b'{"error": "Unprocessable entity"}')
        
        with pytest.raises(APIError) as exc_info:
            handle_api_error(response, "test operation")
        
        error = exc_info.value
        assert error.status_code == 422
        assert error.error_code == "API_422"
        assert "API error for test operation" in str(error)
    
    def test_empty_response_handling(self):
        """Test handling of empty response"""
        response = MockResponse(400, b'')
        
        with pytest.raises(APIError) as exc_info:
            handle_api_error(response, "test operation")
        
        error = exc_info.value
        assert error.status_code == 400
        assert error.response_data == {}


class TestErrorHierarchy:
    """Test error inheritance hierarchy"""
    
    def test_all_errors_inherit_from_connector_error(self):
        """Test that all specific errors inherit from ConnectorError"""
        error_classes = [
            AuthenticationError, ConfigurationError, NetworkError,
            RateLimitError, APIError, ValidationError, TimeoutError,
            ResourceNotFoundError, PermissionError, QuotaExceededError,
            ServiceUnavailableError, DataProcessingError, CacheError,
            RetryExhaustedError
        ]
        
        for error_class in error_classes:
            error = error_class("test")
            assert isinstance(error, ConnectorError)
            assert isinstance(error, Exception)
