"""Tests for SmartSerializer, PickleSerializer, and PassthroughSerializer."""
import pytest
from cache_core.serializer import (
    SmartSerializer,
    PickleSerializer,
    PassthroughSerializer,
)


class TestSmartSerializer:
    def setup_method(self):
        self.s = SmartSerializer()

    # -- Serialize -----------------------------------------------------------

    def test_serialize_int_passthrough(self):
        assert self.s.serialize(42) == 42
        assert self.s.serialize(-1) == -1
        assert self.s.serialize(0) == 0

    def test_serialize_float_passthrough(self):
        assert self.s.serialize(3.14) == 3.14

    def test_serialize_str_passthrough(self):
        assert self.s.serialize("hello") == "hello"
        assert self.s.serialize("") == ""
        assert self.s.serialize('{"a":1}') == '{"a":1}'  # already JSON-looking, still passthrough

    def test_serialize_bool_passthrough(self):
        assert self.s.serialize(True) is True
        assert self.s.serialize(False) is False

    def test_serialize_none_passthrough(self):
        assert self.s.serialize(None) is None

    def test_serialize_dict(self):
        assert self.s.serialize({"a": 1}) == '{"a": 1}'

    def test_serialize_list(self):
        assert self.s.serialize([1, 2, 3]) == "[1, 2, 3]"

    def test_serialize_nested(self):
        val = {"users": [{"id": 1, "tags": ["a", "b"]}]}
        result = self.s.serialize(val)
        assert isinstance(result, str)
        assert '"users"' in result

    # -- Deserialize ---------------------------------------------------------

    def test_deserialize_json_object(self):
        assert self.s.deserialize('{"a": 1}') == {"a": 1}

    def test_deserialize_json_array(self):
        assert self.s.deserialize("[1, 2, 3]") == [1, 2, 3]

    def test_deserialize_json_int(self):
        assert self.s.deserialize("42") == 42

    def test_deserialize_json_float(self):
        assert self.s.deserialize("3.14") == 3.14

    def test_deserialize_json_bool_true(self):
        assert self.s.deserialize("true") is True

    def test_deserialize_json_bool_false(self):
        assert self.s.deserialize("false") is False

    def test_deserialize_json_null(self):
        assert self.s.deserialize("null") is None

    def test_deserialize_json_string(self):
        assert self.s.deserialize('"hello"') == "hello"

    def test_deserialize_plain_string(self):
        assert self.s.deserialize("hello") == "hello"

    def test_deserialize_bytes(self):
        assert self.s.deserialize(b"hello") == "hello"
        assert self.s.deserialize(b"42") == 42

    def test_deserialize_non_string_passthrough(self):
        assert self.s.deserialize(42) == 42
        assert self.s.deserialize(None) is None

    # -- Round-trip --------------------------------------------------------

    def test_roundtrip_dict(self):
        original = {"name": "test", "nested": {"key": [1, 2, 3]}}
        assert self.s.deserialize(self.s.serialize(original)) == original

    def test_roundtrip_list(self):
        original = [{"a": 1}, {"b": 2}]
        assert self.s.deserialize(self.s.serialize(original)) == original

    def test_roundtrip_primitives(self):
        for val in [42, -1, 0, 3.14, "hello", True, False, None]:
            assert self.s.deserialize(self.s.serialize(val)) == val


class _PickleFoo:
    """Module-level class for pickle round-trip testing."""
    def __init__(self, x):
        self.x = x

    def __eq__(self, other):
        return isinstance(other, _PickleFoo) and self.x == other.x


class TestPickleSerializer:
    def test_roundtrip(self):
        s = PickleSerializer()
        original = {"name": "test", "data": [1, 2, 3]}
        result = s.deserialize(s.serialize(original))
        assert result == original

    def test_roundtrip_custom_object(self):
        s = PickleSerializer()
        original = _PickleFoo(42)
        result = s.deserialize(s.serialize(original))
        assert result == original


class TestPassthroughSerializer:
    def test_identity(self):
        s = PassthroughSerializer()
        assert s.serialize(42) == 42
        assert s.serialize("hello") == "hello"
        assert s.serialize({"a": 1}) == {"a": 1}
        assert s.deserialize(42) == 42
        assert s.deserialize("hello") == "hello"
