import pytest
from dataclasses import dataclass
from mca_sdk.utils.serialization import serialize_for_telemetry, _json_default


class UnserializableObject:
    def __str__(self):
        return "UnserializableObject"


@dataclass
class DummyDataClass:
    field1: str
    field2: int


class DictObject:
    def __init__(self):
        self.attr = "value"


class ToListObject:
    def tolist(self):
        return [1, 2, 3]


class ToDictObject:
    def to_dict(self):
        return {"key": "value"}


class FailingToListObject:
    def tolist(self):
        raise ValueError("Failed")


def test_serialize_basic_types():
    assert serialize_for_telemetry(None) is None
    assert serialize_for_telemetry(True) is True
    assert serialize_for_telemetry(123) == 123
    assert serialize_for_telemetry(1.23) == 1.23


def test_serialize_strings():
    assert serialize_for_telemetry("short string") == "short string"
    assert serialize_for_telemetry("a" * 10, max_length=5, truncate_marker="...") == "aa..."
    assert serialize_for_telemetry("a" * 10, max_length=2, truncate_marker="...") == "..."


def test_serialize_complex_types():
    assert serialize_for_telemetry({"key": "value"}) == '{"key": "value"}'
    assert serialize_for_telemetry([1, 2, 3]) == "[1, 2, 3]"
    assert serialize_for_telemetry(UnserializableObject()) == "{}"


def test_serialize_truncation_on_complex_types():
    long_dict = {"key": "a" * 10}
    # length of {"key": "aaaaaaaaaa"} is 21
    assert (
        serialize_for_telemetry(long_dict, max_length=15, truncate_marker="...")
        == '{"key": "aaa...'
    )


def test_json_default_numpy_like():
    assert _json_default(ToListObject()) == [1, 2, 3]


def test_json_default_pandas_like():
    assert _json_default(ToDictObject()) == {"key": "value"}


def test_json_default_dataclass():
    obj = DummyDataClass(field1="test", field2=123)
    assert _json_default(obj) == {"field1": "test", "field2": 123}


def test_json_default_dict_attr():
    obj = DictObject()
    assert _json_default(obj) == {"attr": "value"}


def test_json_default_fallback():
    assert _json_default(UnserializableObject()) == {}


def test_json_default_failing_method():
    # When tolist fails, it should fallback to __dict__ or str()
    obj = FailingToListObject()
    assert _json_default(obj) == {}  # Empty dict because it has no attrs


class NonDictClass:
    # A class that causes json_default fallback to return string instead of dict
    __slots__ = ["foo"]

    def __init__(self):
        self.foo = "bar"

    def __str__(self):
        return "NonDictClass_str"


def test_json_default_string_fallback():
    assert _json_default(NonDictClass()) == "NonDictClass_str"
