"""
Unit tests for the custom exceptions module.
"""

import pytest
from pygeai_gam.exceptions import (
    GAMClientError,
    GAMAuthenticationError,
    GAMAPIError,
    GAMInvalidResponseError,
    GAMMissingParameterError,
)


class TestGAMClientError:
    """Test GAMClientError base exception."""

    def test_init_with_message_only(self):
        """Test initialization with just a message."""
        error = GAMClientError("Test error message")
        assert error.message == "Test error message"
        assert error.status_code is None
        assert error.response is None
        assert str(error) == "Test error message"

    def test_init_with_status_code(self):
        """Test initialization with status code."""
        error = GAMClientError("Test error", status_code=500)
        assert error.status_code == 500
        assert str(error) == "[500] Test error"

    def test_init_with_response(self):
        """Test initialization with response object."""
        mock_response = {"data": "test"}
        error = GAMClientError("Test error", response=mock_response)
        assert error.response == mock_response

    def test_inheritance(self):
        """Test that GAMClientError inherits from Exception."""
        error = GAMClientError("Test")
        assert isinstance(error, Exception)


class TestGAMAuthenticationError:
    """Test GAMAuthenticationError exception."""

    def test_inherits_from_gam_client_error(self):
        """Test that GAMAuthenticationError inherits from GAMClientError."""
        error = GAMAuthenticationError("Auth failed", status_code=401)
        assert isinstance(error, GAMClientError)
        assert isinstance(error, Exception)

    def test_str_with_401_status(self):
        """Test string representation with 401 status."""
        error = GAMAuthenticationError("Unauthorized", status_code=401)
        assert str(error) == "[401] Unauthorized"

    def test_str_with_403_status(self):
        """Test string representation with 403 status."""
        error = GAMAuthenticationError("Forbidden", status_code=403)
        assert str(error) == "[403] Forbidden"


class TestGAMAPIError:
    """Test GAMAPIError exception."""

    def test_inherits_from_gam_client_error(self):
        """Test that GAMAPIError inherits from GAMClientError."""
        error = GAMAPIError("API error", status_code=500)
        assert isinstance(error, GAMClientError)

    def test_with_different_status_codes(self):
        """Test with various HTTP status codes."""
        error_400 = GAMAPIError("Bad request", status_code=400)
        assert error_400.status_code == 400
        assert "[400]" in str(error_400)

        error_404 = GAMAPIError("Not found", status_code=404)
        assert error_404.status_code == 404
        assert "[404]" in str(error_404)

        error_500 = GAMAPIError("Server error", status_code=500)
        assert error_500.status_code == 500
        assert "[500]" in str(error_500)


class TestGAMInvalidResponseError:
    """Test GAMInvalidResponseError exception."""

    def test_inherits_from_gam_client_error(self):
        """Test that GAMInvalidResponseError inherits from GAMClientError."""
        error = GAMInvalidResponseError("Invalid JSON")
        assert isinstance(error, GAMClientError)

    def test_init_without_status_code(self):
        """Test that status_code is always None."""
        error = GAMInvalidResponseError("Parse error")
        assert error.status_code is None
        assert str(error) == "Parse error"

    def test_init_with_response(self):
        """Test initialization with response data."""
        mock_response = "Not valid JSON"
        error = GAMInvalidResponseError("JSON parse failed", response=mock_response)
        assert error.response == mock_response
        assert error.message == "JSON parse failed"


class TestGAMMissingParameterError:
    """Test GAMMissingParameterError exception."""

    def test_inherits_from_gam_client_error(self):
        """Test that GAMMissingParameterError inherits from GAMClientError."""
        error = GAMMissingParameterError("Missing param")
        assert isinstance(error, GAMClientError)

    def test_init_without_parameter_name(self):
        """Test initialization without parameter_name."""
        error = GAMMissingParameterError("Some parameters are missing")
        assert error.parameter_name is None
        assert str(error) == "Some parameters are missing"

    def test_init_with_parameter_name(self):
        """Test initialization with parameter_name."""
        error = GAMMissingParameterError("This field is required", parameter_name="username")
        assert error.parameter_name == "username"
        assert "Missing required parameter: username" in str(error)
        assert "This field is required" in str(error)

    def test_str_format_with_parameter_name(self):
        """Test string format when parameter_name is provided."""
        error = GAMMissingParameterError("Cannot proceed", parameter_name="access_token")
        expected = "Missing required parameter: access_token. Cannot proceed"
        assert str(error) == expected


class TestExceptionHierarchy:
    """Test the exception hierarchy relationships."""

    def test_all_inherit_from_base(self):
        """Test that all exceptions inherit from GAMClientError."""
        errors = [
            GAMAuthenticationError("test"),
            GAMAPIError("test"),
            GAMInvalidResponseError("test"),
            GAMMissingParameterError("test"),
        ]

        for error in errors:
            assert isinstance(error, GAMClientError)
            assert isinstance(error, Exception)

    def test_catch_all_with_base_exception(self):
        """Test that base exception can catch all GAM exceptions."""
        errors = [
            GAMAuthenticationError("auth error", status_code=401),
            GAMAPIError("api error", status_code=500),
            GAMInvalidResponseError("parse error"),
            GAMMissingParameterError("missing param", parameter_name="test"),
        ]

        for error in errors:
            try:
                raise error
            except GAMClientError as e:
                assert isinstance(e, GAMClientError)

    def test_specific_exception_catching(self):
        """Test catching specific exception types."""
        auth_error = GAMAuthenticationError("auth failed", status_code=401)

        with pytest.raises(GAMAuthenticationError):
            raise auth_error

        try:
            raise auth_error
        except GAMAuthenticationError as e:
            assert e.status_code == 401
            assert isinstance(e, GAMClientError)
