"""
Tests for encoder.py - JSON encoding utilities.

This test suite provides comprehensive coverage for the Encoder class:
- Tests serialization of various Python types
- Tests special handling of Path objects
- Tests DateRange serialization
- Tests date serialization
- Tests ImpersonateTarget serialization
- Tests ItemDTO serialization
- Tests object serialization with serialize() method
- Tests object serialization with __dict__ fallback
- Tests fallback to default JSONEncoder behavior

Total test functions: 10
All supported types and edge cases are covered.
"""

import json
from datetime import date
from pathlib import Path
from unittest.mock import MagicMock

import pytest

from app.library.encoder import Encoder


class TestEncoder:
    """Test the Encoder class."""

    def setup_method(self):
        """Set up test fixtures."""
        self.encoder = Encoder()

    def test_path_serialization(self):
        """Test that Path objects are serialized as strings."""
        path = Path("/tmp/test/file.txt")
        result = self.encoder.default(path)
        assert result == "/tmp/test/file.txt"
        assert isinstance(result, str)

    def test_path_serialization_relative(self):
        """Test that relative Path objects are serialized correctly."""
        path = Path("relative/path/file.txt")
        result = self.encoder.default(path)
        assert result == "relative/path/file.txt"

    def test_date_serialization(self):
        """Test that date objects are serialized as strings."""
        test_date = date(2024, 3, 15)
        result = self.encoder.default(test_date)
        assert result == "2024-03-15"

    def test_object_with_serialize_method(self):
        """Test that objects with serialize method use it."""

        class CustomObject:
            def serialize(self):
                return {"custom": "data", "type": "test"}

        obj = CustomObject()
        result = self.encoder.default(obj)
        assert result == {"custom": "data", "type": "test"}

    def test_object_with_dict_fallback(self):
        """Test that objects without serialize method fall back to __dict__."""

        class SimpleObject:
            def __init__(self):
                self.name = "test"
                self.value = 42

        obj = SimpleObject()
        result = self.encoder.default(obj)
        assert result == {"name": "test", "value": 42}

    def test_object_without_dict_fallback_to_default(self):
        """Test that objects without __dict__ fall back to default JSONEncoder."""
        # This should raise TypeError since complex is not JSON serializable
        with pytest.raises(TypeError):
            self.encoder.default(complex(1, 2))

    def test_json_dumps_integration(self):
        """Test full JSON serialization with various types."""
        data = {"path": Path("/tmp/test.txt"), "date": date(2024, 1, 1), "number": 42, "string": "test"}

        result = json.dumps(data, cls=Encoder)
        parsed = json.loads(result)

        assert parsed["path"] == "/tmp/test.txt"
        assert parsed["date"] == "2024-01-01"
        assert parsed["number"] == 42
        assert parsed["string"] == "test"

    def test_json_dumps_with_custom_object(self):
        """Test JSON serialization with custom objects."""

        class TestObject:
            def __init__(self):
                self.name = "test"
                self.items = [1, 2, 3]

        data = {"object": TestObject(), "regular": "data"}

        result = json.dumps(data, cls=Encoder)
        parsed = json.loads(result)

        assert parsed["object"]["name"] == "test"
        assert parsed["object"]["items"] == [1, 2, 3]
        assert parsed["regular"] == "data"

    def test_nested_serialization(self):
        """Test serialization of nested structures with various types."""

        class CustomObj:
            def serialize(self):
                return {"serialized": True}

        data = {
            "paths": [Path("/tmp/1.txt"), Path("/tmp/2.txt")],
            "dates": [date(2024, 1, 1), date(2024, 12, 31)],
            "custom": CustomObj(),
            "nested": {"path": Path("/nested/path"), "date": date(2024, 6, 15)},
        }

        result = json.dumps(data, cls=Encoder)
        parsed = json.loads(result)

        assert parsed["paths"] == ["/tmp/1.txt", "/tmp/2.txt"]
        assert parsed["dates"] == ["2024-01-01", "2024-12-31"]
        assert parsed["custom"] == {"serialized": True}
        assert parsed["nested"]["path"] == "/nested/path"
        assert parsed["nested"]["date"] == "2024-06-15"

    def test_mock_daterange_serialization(self):
        """Test DateRange serialization with mock object."""
        # Mock a DateRange-like object
        mock_daterange = MagicMock()
        mock_daterange.start = date(2024, 1, 15)
        mock_daterange.end = date(2024, 12, 31)

        # Mock isinstance to return True for DateRange
        import builtins

        original_isinstance = builtins.isinstance

        def mock_isinstance(obj, cls):
            if obj is mock_daterange and hasattr(cls, "__name__") and "DateRange" in str(cls):
                return True
            return original_isinstance(obj, cls)

        builtins.isinstance = mock_isinstance

        try:
            result = self.encoder.default(mock_daterange)
            expected = {"start": "20240115", "end": "20241231"}
            assert result == expected
        finally:
            builtins.isinstance = original_isinstance


if __name__ == "__main__":
    pytest.main([__file__])
