"""
Tests for SDK compression functionality (Phase 3b).

Tests:
- _maybe_compress: Large payloads get compressed
- _maybe_compress: Small payloads stay uncompressed
- Round-trip: compress -> decompress recovers original data
"""

import pytest
import json
import gzip
import base64


class TestMaybeCompress:
    """Tests for SDK-side compression logic."""

    def test_compress_large_payload(self):
        """Large payloads (>1KB) should be compressed with gzip + base64."""
        # Import the function (will fail initially - TDD Red)
        from swisper_studio_sdk.tracing.compression import maybe_compress
        
        # Create payload > 1KB (lots of repeated data compresses well)
        large_data = {
            "messages": [{"role": "user", "content": "x" * 500}] * 5,
            "metadata": {"key": "value" * 100}
        }
        original_json = json.dumps(large_data)
        assert len(original_json) > 1024, "Test setup: payload must be >1KB"
        
        # Compress
        result = maybe_compress(large_data)
        
        # Assertions
        assert result.get("_compressed") is True, "Should have _compressed flag"
        assert "_data" in result, "Should have _data field with compressed content"
        
        # Verify compressed size is smaller
        compressed_size = len(result["_data"])
        assert compressed_size < len(original_json), "Compressed should be smaller"

    def test_skip_compression_small_payload(self):
        """Small payloads (<1KB) should NOT be compressed."""
        from swisper_studio_sdk.tracing.compression import maybe_compress
        
        # Create small payload < 1KB
        small_data = {"status": "ok", "count": 42}
        original_json = json.dumps(small_data)
        assert len(original_json) < 1024, "Test setup: payload must be <1KB"
        
        # Process
        result = maybe_compress(small_data)
        
        # Assertions - should be unchanged
        assert "_compressed" not in result, "Should NOT have _compressed flag"
        assert result == small_data, "Small payload should be unchanged"

    def test_compression_threshold_boundary(self):
        """Test behavior at exactly 1KB threshold."""
        from swisper_studio_sdk.tracing.compression import maybe_compress, COMPRESSION_THRESHOLD
        
        # Create payload just under threshold
        under_threshold = {"data": "x" * (COMPRESSION_THRESHOLD - 50)}
        result_under = maybe_compress(under_threshold)
        assert "_compressed" not in result_under, "Under threshold: no compression"
        
        # Create payload just over threshold
        over_threshold = {"data": "x" * (COMPRESSION_THRESHOLD + 100)}
        result_over = maybe_compress(over_threshold)
        assert result_over.get("_compressed") is True, "Over threshold: should compress"

    def test_compression_handles_empty_dict(self):
        """Empty dict should pass through without compression."""
        from swisper_studio_sdk.tracing.compression import maybe_compress
        
        result = maybe_compress({})
        assert result == {}, "Empty dict should be unchanged"
        assert "_compressed" not in result

    def test_compression_handles_none(self):
        """None should pass through without error."""
        from swisper_studio_sdk.tracing.compression import maybe_compress
        
        result = maybe_compress(None)
        assert result is None, "None should return None"


class TestMaybeDecompress:
    """Tests for SDK/Backend decompression logic (used by backend consumer)."""

    def test_decompress_compressed_event(self):
        """Compressed events should be decompressed correctly."""
        from swisper_studio_sdk.tracing.compression import maybe_compress, maybe_decompress
        
        # Create and compress large payload
        original_data = {
            "messages": [{"role": "user", "content": "Hello " * 200}],
            "tokens": {"prompt": 100, "completion": 50}
        }
        
        compressed = maybe_compress(original_data)
        assert compressed.get("_compressed") is True, "Setup: should be compressed"
        
        # Decompress
        decompressed = maybe_decompress(compressed)
        
        # Verify round-trip
        assert decompressed == original_data, "Decompressed should match original"

    def test_decompress_uncompressed_event(self):
        """Uncompressed events should pass through unchanged (backward compat)."""
        from swisper_studio_sdk.tracing.compression import maybe_decompress
        
        # Normal event without compression flag
        normal_event = {"type": "observation_end", "output": {"result": "success"}}
        
        result = maybe_decompress(normal_event)
        
        assert result == normal_event, "Uncompressed event should be unchanged"

    def test_decompress_handles_none(self):
        """None should pass through without error."""
        from swisper_studio_sdk.tracing.compression import maybe_decompress
        
        result = maybe_decompress(None)
        assert result is None

    def test_decompress_handles_invalid_compressed_data(self):
        """Invalid compressed data should return original (graceful degradation)."""
        from swisper_studio_sdk.tracing.compression import maybe_decompress
        
        # Malformed compressed event (invalid base64)
        invalid_event = {"_compressed": True, "_data": "not-valid-base64!!!"}
        
        # Should not raise, return something usable
        result = maybe_decompress(invalid_event)
        # Either returns the original or raises gracefully
        assert result is not None


class TestCompressionRoundTrip:
    """End-to-end compression/decompression tests."""

    def test_roundtrip_preserves_nested_structures(self):
        """Complex nested structures should survive compression round-trip."""
        from swisper_studio_sdk.tracing.compression import maybe_compress, maybe_decompress
        
        complex_data = {
            "level1": {
                "level2": {
                    "level3": {
                        "array": [1, 2, {"nested": True}],
                        "string": "x" * 1000
                    }
                }
            },
            "unicode": "Hello 世界 🌍",
            "numbers": [1.5, -100, 0, 999999999999],
            "booleans": [True, False, None]
        }
        
        compressed = maybe_compress(complex_data)
        decompressed = maybe_decompress(compressed)
        
        assert decompressed == complex_data, "Complex structures should survive round-trip"

    def test_roundtrip_preserves_datetime_strings(self):
        """ISO datetime strings should survive compression."""
        from swisper_studio_sdk.tracing.compression import maybe_compress, maybe_decompress
        
        data_with_dates = {
            "start_time": "2024-01-15T10:30:00.123456",
            "end_time": "2024-01-15T10:30:05.789012",
            "padding": "x" * 1000  # Force compression
        }
        
        compressed = maybe_compress(data_with_dates)
        decompressed = maybe_decompress(compressed)
        
        assert decompressed["start_time"] == "2024-01-15T10:30:00.123456"
        assert decompressed["end_time"] == "2024-01-15T10:30:05.789012"
