"""领域层异常类单元测试."""

import pytest

from packages.domain.exceptions import (
    DomainError,
    NotFoundError,
    QuotaExceededError,
    ValidationError,
)


class TestDomainError:
    """领域异常基类测试."""

    def test_is_exception(self):
        """DomainError 继承自 Exception."""
        err = DomainError("test")
        assert isinstance(err, Exception)

    def test_message(self):
        """可以设置错误消息."""
        err = DomainError("something wrong")
        assert str(err) == "something wrong"

    def test_empty_message(self):
        """支持空消息."""
        err = DomainError()
        assert str(err) == ""

    def test_can_be_raised(self):
        """可以被 raise 和 catch."""
        with pytest.raises(DomainError) as exc_info:
            raise DomainError("oops")
        assert str(exc_info.value) == "oops"


class TestNotFoundError:
    """资源不存在异常测试."""

    def test_inherits_domain_error(self):
        """NotFoundError 继承自 DomainError."""
        err = NotFoundError("user not found")
        assert isinstance(err, DomainError)
        assert isinstance(err, Exception)

    def test_message(self):
        """错误消息正确."""
        err = NotFoundError("project 123 not found")
        assert str(err) == "project 123 not found"
        assert "123" in str(err)

    def test_can_catch_as_domain_error(self):
        """可以用 DomainError 捕获."""
        with pytest.raises(DomainError):
            raise NotFoundError("not found")


class TestValidationError:
    """校验失败异常测试."""

    def test_inherits_domain_error(self):
        """ValidationError 继承自 DomainError."""
        err = ValidationError("invalid input")
        assert isinstance(err, DomainError)

    def test_message(self):
        """错误消息正确."""
        msg = "name must not be empty"
        err = ValidationError(msg)
        assert str(err) == msg

    def test_not_not_found(self):
        """ValidationError 不是 NotFoundError."""
        err = ValidationError("bad")
        assert not isinstance(err, NotFoundError)


class TestQuotaExceededError:
    """配额超限异常测试."""

    def test_inherits_domain_error(self):
        """QuotaExceededError 继承自 DomainError."""
        err = QuotaExceededError("storage", 100.0, 150.0)
        assert isinstance(err, DomainError)

    def test_dimension_attribute(self):
        """保存 dimension 属性."""
        err = QuotaExceededError("storage", 100.0, 150.0)
        assert err.dimension == "storage"

    def test_limit_attribute(self):
        """保存 limit 属性."""
        err = QuotaExceededError("storage", 100.0, 150.0)
        assert err.limit == 100.0

    def test_used_attribute(self):
        """保存 used 属性."""
        err = QuotaExceededError("storage", 100.0, 150.0)
        assert err.used == 150.0

    def test_message_format(self):
        """错误消息格式正确."""
        err = QuotaExceededError("credits", 50.0, 75.0)
        msg = str(err)
        assert "credits" in msg
        assert "50" in msg
        assert "75" in msg
        assert "Quota exceeded" in msg

    def test_zero_limit(self):
        """limit 为 0 的情况."""
        err = QuotaExceededError("test", 0.0, 1.0)
        assert err.limit == 0.0
        assert err.used == 1.0
        assert "0" in str(err)

    def test_equal_limit_and_used(self):
        """used 刚好等于 limit（边界情况）."""
        err = QuotaExceededError("test", 100.0, 100.0)
        assert err.used == 100.0
        assert err.limit == 100.0

    def test_integer_values(self):
        """整数值也能正常工作."""
        err = QuotaExceededError("count", 10, 20)
        assert err.dimension == "count"
        assert err.limit == 10
        assert err.used == 20

    def test_can_catch_as_domain_error(self):
        """可以用 DomainError 捕获."""
        with pytest.raises(DomainError):
            raise QuotaExceededError("x", 1.0, 2.0)


class TestExceptionHierarchy:
    """异常继承关系验证."""

    def test_all_are_domain_errors(self):
        """所有领域异常都是 DomainError."""
        errors = [
            NotFoundError("test"),
            ValidationError("test"),
            QuotaExceededError("test", 1, 2),
        ]
        for err in errors:
            assert isinstance(err, DomainError)

    def test_all_are_exceptions(self):
        """所有领域异常都是 Exception."""
        errors = [
            DomainError("test"),
            NotFoundError("test"),
            ValidationError("test"),
            QuotaExceededError("test", 1, 2),
        ]
        for err in errors:
            assert isinstance(err, Exception)

    def test_not_found_is_not_validation(self):
        """不同异常类型不能互相混淆."""
        assert not isinstance(NotFoundError("x"), ValidationError)
        assert not isinstance(ValidationError("x"), NotFoundError)
        assert not isinstance(QuotaExceededError("x", 1, 2), NotFoundError)
        assert not isinstance(QuotaExceededError("x", 1, 2), ValidationError)
