# -*- coding: utf-8 -*-
# Copyright (C) Alexander Pace, Daniel Wysocki (2026)
#
# This file is part of gracedb
#
# gracedb is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# It is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gracedb.  If not, see <http://www.gnu.org/licenses/>.
"""Tests for Kafka exceptions."""

import pytest
from ligo.gracedb.kafka.exceptions import (
    KafkaError,
    KafkaAuthenticationError,
    KafkaTokenError,
    KafkaProducerError,
    KafkaConnectionError,
    KafkaMessageDeliveryError,
)


class TestExceptionHierarchy:
    """Test exception inheritance hierarchy."""

    def test_kafka_error_is_base(self):
        """Test that KafkaError is the base exception."""
        assert issubclass(KafkaAuthenticationError, KafkaError)
        assert issubclass(KafkaProducerError, KafkaError)
        assert issubclass(KafkaConnectionError, KafkaError)

    def test_token_error_inherits_from_auth_error(self):
        """Test that KafkaTokenError inherits from KafkaAuthenticationError."""
        assert issubclass(KafkaTokenError, KafkaAuthenticationError)
        assert issubclass(KafkaTokenError, KafkaError)

    def test_message_delivery_error_inherits_from_producer_error(self):
        """Test that KafkaMessageDeliveryError inherits from KafkaProducerError."""
        assert issubclass(KafkaMessageDeliveryError, KafkaProducerError)
        assert issubclass(KafkaMessageDeliveryError, KafkaError)

    def test_all_inherit_from_exception(self):
        """Test that all Kafka exceptions inherit from Exception."""
        exceptions = [
            KafkaError,
            KafkaAuthenticationError,
            KafkaTokenError,
            KafkaProducerError,
            KafkaConnectionError,
            KafkaMessageDeliveryError,
        ]
        for exc_class in exceptions:
            assert issubclass(exc_class, Exception)


class TestExceptionRaising:
    """Test raising and catching exceptions."""

    def test_raise_kafka_error(self):
        """Test raising KafkaError."""
        with pytest.raises(KafkaError) as exc_info:
            raise KafkaError("Test error")
        assert str(exc_info.value) == "Test error"

    def test_raise_auth_error(self):
        """Test raising KafkaAuthenticationError."""
        with pytest.raises(KafkaAuthenticationError) as exc_info:
            raise KafkaAuthenticationError("Auth failed")
        assert "Auth failed" in str(exc_info.value)

    def test_raise_token_error(self):
        """Test raising KafkaTokenError."""
        with pytest.raises(KafkaTokenError) as exc_info:
            raise KafkaTokenError("No token found")
        assert "No token found" in str(exc_info.value)

    def test_catch_token_error_as_auth_error(self):
        """Test that KafkaTokenError can be caught as KafkaAuthenticationError."""
        with pytest.raises(KafkaAuthenticationError):
            raise KafkaTokenError("Token error")

    def test_catch_token_error_as_kafka_error(self):
        """Test that KafkaTokenError can be caught as KafkaError."""
        with pytest.raises(KafkaError):
            raise KafkaTokenError("Token error")

    def test_raise_producer_error(self):
        """Test raising KafkaProducerError."""
        with pytest.raises(KafkaProducerError) as exc_info:
            raise KafkaProducerError("Producer failed")
        assert "Producer failed" in str(exc_info.value)

    def test_raise_connection_error(self):
        """Test raising KafkaConnectionError."""
        with pytest.raises(KafkaConnectionError) as exc_info:
            raise KafkaConnectionError("Connection refused")
        assert "Connection refused" in str(exc_info.value)

    def test_raise_delivery_error(self):
        """Test raising KafkaMessageDeliveryError."""
        with pytest.raises(KafkaMessageDeliveryError) as exc_info:
            raise KafkaMessageDeliveryError("Delivery failed")
        assert "Delivery failed" in str(exc_info.value)

    def test_catch_delivery_error_as_producer_error(self):
        """Test that KafkaMessageDeliveryError can be caught as KafkaProducerError."""
        with pytest.raises(KafkaProducerError):
            raise KafkaMessageDeliveryError("Delivery error")


class TestExceptionMessages:
    """Test exception message handling."""

    def test_exception_with_format_string(self):
        """Test exception with formatted message."""
        broker = "localhost:9092"
        with pytest.raises(KafkaConnectionError) as exc_info:
            raise KafkaConnectionError(f"Failed to connect to {broker}")
        assert "localhost:9092" in str(exc_info.value)

    def test_exception_chaining(self):
        """Test exception chaining with 'from'."""
        original_error = ValueError("Original error")
        with pytest.raises(KafkaTokenError) as exc_info:
            try:
                raise original_error
            except ValueError as e:
                raise KafkaTokenError("Token error") from e

        assert exc_info.value.__cause__ is original_error
