"""
描述:
- serde.common.error 模块的单元测试。
- 测试所有 8 个自定义异常类的继承、实例化和使用。
"""

from __future__ import annotations

import pytest

from xfintech.serde.common.error import (
    DeserialiserFailedError,
    DeserialiserImportError,
    DeserialiserInputTypeError,
    DeserialiserNotSupportedError,
    SerialiserFailedError,
    SerialiserImportError,
    SerialiserInputTypeError,
    SerialiserNotSupportedError,
)

# =============================================================================
# DeserialiserInputTypeError Tests
# =============================================================================


def test_deserialiserinputtypeerror_inherits_from_typeerror():
    """测试 DeserialiserInputTypeError 继承自 TypeError"""
    assert issubclass(DeserialiserInputTypeError, TypeError)


def test_deserialiserinputtypeerror_can_instantiate():
    """测试可以实例化 DeserialiserInputTypeError"""
    error = DeserialiserInputTypeError("Test message")
    assert isinstance(error, DeserialiserInputTypeError)
    assert isinstance(error, TypeError)


def test_deserialiserinputtypeerror_can_raise():
    """测试可以抛出 DeserialiserInputTypeError"""
    with pytest.raises(DeserialiserInputTypeError):
        raise DeserialiserInputTypeError("Input type is wrong")


def test_deserialiserinputtypeerror_message():
    """测试 DeserialiserInputTypeError 消息"""
    msg = "Expected bytes, got str"
    error = DeserialiserInputTypeError(msg)
    assert str(error) == msg


def test_deserialiserinputtypeerror_can_catch_as_typeerror():
    """测试可以作为 TypeError 捕获"""
    with pytest.raises(TypeError):
        raise DeserialiserInputTypeError("Wrong type")


def test_deserialiserinputtypeerror_with_multiple_args():
    """测试多参数实例化"""
    error = DeserialiserInputTypeError("Error", "Extra info")
    assert len(error.args) == 2


def test_deserialiserinputtypeerror_empty_message():
    """测试空消息"""
    error = DeserialiserInputTypeError()
    assert str(error) == ""


def test_deserialiserinputtypeerror_use_case():
    """测试实际使用场景"""

    def deserialise(data):
        if not isinstance(data, bytes):
            raise DeserialiserInputTypeError(f"Expected bytes, got {type(data).__name__}")

    with pytest.raises(DeserialiserInputTypeError) as excinfo:
        deserialise("not bytes")
    assert "Expected bytes" in str(excinfo.value)


# =============================================================================
# DeserialiserNotSupportedError Tests
# =============================================================================


def test_deserialisernotsupportederror_inherits_from_typeerror():
    """测试 DeserialiserNotSupportedError 继承自 TypeError"""
    assert issubclass(DeserialiserNotSupportedError, TypeError)


def test_deserialisernotsupportederror_can_instantiate():
    """测试可以实例化 DeserialiserNotSupportedError"""
    error = DeserialiserNotSupportedError("Test message")
    assert isinstance(error, DeserialiserNotSupportedError)
    assert isinstance(error, TypeError)


def test_deserialisernotsupportederror_can_raise():
    """测试可以抛出 DeserialiserNotSupportedError"""
    with pytest.raises(DeserialiserNotSupportedError):
        raise DeserialiserNotSupportedError("Format not supported")


def test_deserialisernotsupportederror_message():
    """测试 DeserialiserNotSupportedError 消息"""
    msg = "XML format not supported"
    error = DeserialiserNotSupportedError(msg)
    assert str(error) == msg


def test_deserialisernotsupportederror_use_case():
    """测试实际使用场景"""

    def deserialise(data, format):
        supported = ["json", "csv", "parquet"]
        if format not in supported:
            raise DeserialiserNotSupportedError(f"Format '{format}' not supported. Supported: {supported}")

    with pytest.raises(DeserialiserNotSupportedError) as excinfo:
        deserialise(b"data", "xml")
    assert "xml" in str(excinfo.value)


# =============================================================================
# DeserialiserImportError Tests
# =============================================================================


def test_deserialiserimporterror_inherits_from_importerror():
    """测试 DeserialiserImportError 继承自 ImportError"""
    assert issubclass(DeserialiserImportError, ImportError)


def test_deserialiserimporterror_can_instantiate():
    """测试可以实例化 DeserialiserImportError"""
    error = DeserialiserImportError("Test message")
    assert isinstance(error, DeserialiserImportError)
    assert isinstance(error, ImportError)


def test_deserialiserimporterror_can_raise():
    """测试可以抛出 DeserialiserImportError"""
    with pytest.raises(DeserialiserImportError):
        raise DeserialiserImportError("Missing dependency")


def test_deserialiserimporterror_message():
    """测试 DeserialiserImportError 消息"""
    msg = "pyarrow is required"
    error = DeserialiserImportError(msg)
    assert str(error) == msg


def test_deserialiserimporterror_can_catch_as_importerror():
    """测试可以作为 ImportError 捕获"""
    with pytest.raises(ImportError):
        raise DeserialiserImportError("Missing module")


def test_deserialiserimporterror_with_cause():
    """测试带原因链的异常"""
    original = ImportError("No module named 'pandas'")
    try:
        raise DeserialiserImportError("pandas required") from original
    except DeserialiserImportError as e:
        assert e.__cause__ is original


# =============================================================================
# DeserialiserFailedError Tests
# =============================================================================


def test_deserialisedfailederror_inherits_from_exception():
    """测试 DeserialiserFailedError 继承自 Exception"""
    assert issubclass(DeserialiserFailedError, Exception)


def test_deserialisedfailederror_can_instantiate():
    """测试可以实例化 DeserialiserFailedError"""
    error = DeserialiserFailedError("Test message")
    assert isinstance(error, DeserialiserFailedError)
    assert isinstance(error, Exception)


def test_deserialisedfailederror_can_raise():
    """测试可以抛出 DeserialiserFailedError"""
    with pytest.raises(DeserialiserFailedError):
        raise DeserialiserFailedError("Deserialisation failed")


def test_deserialisedfailederror_message():
    """测试 DeserialiserFailedError 消息"""
    msg = "Failed to parse data"
    error = DeserialiserFailedError(msg)
    assert str(error) == msg


def test_deserialisedfailederror_not_typeerror():
    """测试不是 TypeError"""
    error = DeserialiserFailedError()
    assert not isinstance(error, TypeError)


def test_deserialisedfailederror_use_case():
    """测试实际使用场景"""
    import json

    def deserialise_json(data):
        try:
            return json.loads(data)
        except json.JSONDecodeError as e:
            raise DeserialiserFailedError(f"Failed to deserialise JSON: {e}") from e

    with pytest.raises(DeserialiserFailedError) as excinfo:
        deserialise_json(b"invalid json")
    assert "Failed to deserialise JSON" in str(excinfo.value)


def test_deserialisedfailederror_with_cause():
    """测试带原因链的异常"""
    original = ValueError("Invalid data")
    try:
        raise DeserialiserFailedError("Parsing failed") from original
    except DeserialiserFailedError as e:
        assert e.__cause__ is original


# =============================================================================
# SerialiserInputTypeError Tests
# =============================================================================


def test_serialiserinputtypeerror_inherits_from_typeerror():
    """测试 SerialiserInputTypeError 继承自 TypeError"""
    assert issubclass(SerialiserInputTypeError, TypeError)


def test_serialiserinputtypeerror_can_instantiate():
    """测试可以实例化 SerialiserInputTypeError"""
    error = SerialiserInputTypeError("Test message")
    assert isinstance(error, SerialiserInputTypeError)
    assert isinstance(error, TypeError)


def test_serialiserinputtypeerror_can_raise():
    """测试可以抛出 SerialiserInputTypeError"""
    with pytest.raises(SerialiserInputTypeError):
        raise SerialiserInputTypeError("Input type is wrong")


def test_serialiserinputtypeerror_message():
    """测试 SerialiserInputTypeError 消息"""
    msg = "Expected DataFrame, got list"
    error = SerialiserInputTypeError(msg)
    assert str(error) == msg


def test_serialiserinputtypeerror_can_catch_as_typeerror():
    """测试可以作为 TypeError 捕获"""
    with pytest.raises(TypeError):
        raise SerialiserInputTypeError("Wrong type")


def test_serialiserinputtypeerror_use_case():
    """测试实际使用场景"""

    def serialise(data):
        if not hasattr(data, "to_dict"):
            raise SerialiserInputTypeError(f"Expected DataFrame-like object, got {type(data).__name__}")

    with pytest.raises(SerialiserInputTypeError) as excinfo:
        serialise([1, 2, 3])
    assert "Expected DataFrame-like" in str(excinfo.value)


# =============================================================================
# SerialiserNotSupportedError Tests
# =============================================================================


def test_serialisernotsupportederror_inherits_from_typeerror():
    """测试 SerialiserNotSupportedError 继承自 TypeError"""
    assert issubclass(SerialiserNotSupportedError, TypeError)


def test_serialisernotsupportederror_can_instantiate():
    """测试可以实例化 SerialiserNotSupportedError"""
    error = SerialiserNotSupportedError("Test message")
    assert isinstance(error, SerialiserNotSupportedError)
    assert isinstance(error, TypeError)


def test_serialisernotsupportederror_can_raise():
    """测试可以抛出 SerialiserNotSupportedError"""
    with pytest.raises(SerialiserNotSupportedError):
        raise SerialiserNotSupportedError("Format not supported")


def test_serialisernotsupportederror_message():
    """测试 SerialiserNotSupportedError 消息"""
    msg = "XML format not supported"
    error = SerialiserNotSupportedError(msg)
    assert str(error) == msg


def test_serialisernotsupportederror_use_case():
    """测试实际使用场景"""

    def serialise(data, format):
        supported = ["json", "csv", "parquet"]
        if format not in supported:
            raise SerialiserNotSupportedError(f"Format '{format}' not supported. Supported: {supported}")

    with pytest.raises(SerialiserNotSupportedError) as excinfo:
        serialise({}, "xml")
    assert "xml" in str(excinfo.value)


# =============================================================================
# SerialiserImportError Tests
# =============================================================================


def test_serialiserimporterror_inherits_from_importerror():
    """测试 SerialiserImportError 继承自 ImportError"""
    assert issubclass(SerialiserImportError, ImportError)


def test_serialiserimporterror_can_instantiate():
    """测试可以实例化 SerialiserImportError"""
    error = SerialiserImportError("Test message")
    assert isinstance(error, SerialiserImportError)
    assert isinstance(error, ImportError)


def test_serialiserimporterror_can_raise():
    """测试可以抛出 SerialiserImportError"""
    with pytest.raises(SerialiserImportError):
        raise SerialiserImportError("Missing dependency")


def test_serialiserimporterror_message():
    """测试 SerialiserImportError 消息"""
    msg = "pyarrow is required"
    error = SerialiserImportError(msg)
    assert str(error) == msg


def test_serialiserimporterror_can_catch_as_importerror():
    """测试可以作为 ImportError 捕获"""
    with pytest.raises(ImportError):
        raise SerialiserImportError("Missing module")


def test_serialiserimporterror_with_cause():
    """测试带原因链的异常"""
    original = ImportError("No module named 'pandas'")
    try:
        raise SerialiserImportError("pandas required") from original
    except SerialiserImportError as e:
        assert e.__cause__ is original


# =============================================================================
# SerialiserFailedError Tests
# =============================================================================


def test_serialiserfailederror_inherits_from_exception():
    """测试 SerialiserFailedError 继承自 Exception"""
    assert issubclass(SerialiserFailedError, Exception)


def test_serialiserfailederror_can_instantiate():
    """测试可以实例化 SerialiserFailedError"""
    error = SerialiserFailedError("Test message")
    assert isinstance(error, SerialiserFailedError)
    assert isinstance(error, Exception)


def test_serialiserfailederror_can_raise():
    """测试可以抛出 SerialiserFailedError"""
    with pytest.raises(SerialiserFailedError):
        raise SerialiserFailedError("Serialisation failed")


def test_serialiserfailederror_message():
    """测试 SerialiserFailedError 消息"""
    msg = "Failed to convert data"
    error = SerialiserFailedError(msg)
    assert str(error) == msg


def test_serialiserfailederror_not_typeerror():
    """测试不是 TypeError"""
    error = SerialiserFailedError()
    assert not isinstance(error, TypeError)


def test_serialiserfailederror_use_case():
    """测试实际使用场景"""
    import json

    def serialise_json(data):
        try:
            return json.dumps(data).encode()
        except (TypeError, ValueError) as e:
            raise SerialiserFailedError(f"Failed to serialise to JSON: {e}") from e

    # Create non-serializable object
    class NonSerializable:
        pass

    with pytest.raises(SerialiserFailedError) as excinfo:
        serialise_json({"obj": NonSerializable()})
    assert "Failed to serialise to JSON" in str(excinfo.value)


def test_serialiserfailederror_with_cause():
    """测试带原因链的异常"""
    original = ValueError("Invalid data")
    try:
        raise SerialiserFailedError("Conversion failed") from original
    except SerialiserFailedError as e:
        assert e.__cause__ is original


# =============================================================================
# Cross-Error Distinction Tests
# =============================================================================


def test_deserialiser_and_serialiser_input_type_errors_are_distinct():
    """测试反序列化和序列化输入类型错误是不同的类"""
    assert DeserialiserInputTypeError is not SerialiserInputTypeError
    assert not issubclass(DeserialiserInputTypeError, SerialiserInputTypeError)
    assert not issubclass(SerialiserInputTypeError, DeserialiserInputTypeError)


def test_deserialiser_and_serialiser_not_supported_errors_are_distinct():
    """测试反序列化和序列化不支持错误是不同的类"""
    assert DeserialiserNotSupportedError is not SerialiserNotSupportedError
    assert not issubclass(DeserialiserNotSupportedError, SerialiserNotSupportedError)
    assert not issubclass(SerialiserNotSupportedError, DeserialiserNotSupportedError)


def test_deserialiser_and_serialiser_import_errors_are_distinct():
    """测试反序列化和序列化导入错误是不同的类"""
    assert DeserialiserImportError is not SerialiserImportError
    assert not issubclass(DeserialiserImportError, SerialiserImportError)
    assert not issubclass(SerialiserImportError, DeserialiserImportError)


def test_deserialiser_and_serialiser_failed_errors_are_distinct():
    """测试反序列化和序列化失败错误是不同的类"""
    assert DeserialiserFailedError is not SerialiserFailedError
    assert not issubclass(DeserialiserFailedError, SerialiserFailedError)
    assert not issubclass(SerialiserFailedError, DeserialiserFailedError)


# =============================================================================
# Error Hierarchy Tests
# =============================================================================


def test_all_typeerror_subclasses():
    """测试所有 TypeError 子类"""
    typeerror_subclasses = [
        DeserialiserInputTypeError,
        DeserialiserNotSupportedError,
        SerialiserInputTypeError,
        SerialiserNotSupportedError,
    ]
    for error_class in typeerror_subclasses:
        assert issubclass(error_class, TypeError)


def test_all_importerror_subclasses():
    """测试所有 ImportError 子类"""
    importerror_subclasses = [
        DeserialiserImportError,
        SerialiserImportError,
    ]
    for error_class in importerror_subclasses:
        assert issubclass(error_class, ImportError)


def test_all_exception_subclasses():
    """测试所有 Exception 子类（不是 TypeError 或 ImportError）"""
    exception_subclasses = [
        DeserialiserFailedError,
        SerialiserFailedError,
    ]
    for error_class in exception_subclasses:
        assert issubclass(error_class, Exception)
        assert not issubclass(error_class, TypeError)
        assert not issubclass(error_class, ImportError)


def test_error_catching_with_multiple_types():
    """测试捕获多种错误类型"""

    def operation():
        raise DeserialiserInputTypeError("Type error")

    # Can catch as specific type
    with pytest.raises(DeserialiserInputTypeError):
        operation()

    # Can catch as TypeError
    with pytest.raises(TypeError):
        operation()

    # Can catch as Exception
    with pytest.raises(Exception):
        operation()
