"""Tests for MessageDeduplicator TTL enforcement (#10306).

Previously, is_duplicate() returned True for any previously seen ID without
checking its age — expired entries were only purged when cache size exceeded
max_size.  Normal workloads never overflowed, so messages stayed "duplicate"
forever.

The fix checks TTL at query time: if the entry's timestamp plus TTL is in
the past, the entry is treated as expired and the message is allowed through.
"""

import time

from gateway.platforms.helpers import MessageDeduplicator


class TestMessageDeduplicatorTTL:
    """TTL-based expiration must work regardless of cache size."""

    def test_duplicate_within_ttl(self):
        """Same message within TTL window is duplicate."""
        dedup = MessageDeduplicator(ttl_seconds=60)
        assert dedup.is_duplicate("msg-1") is False
        assert dedup.is_duplicate("msg-1") is True

    def test_not_duplicate_after_ttl_expires(self):
        """Same message AFTER TTL expires should NOT be duplicate."""
        dedup = MessageDeduplicator(ttl_seconds=5)
        assert dedup.is_duplicate("msg-1") is False

        # Fast-forward time past TTL
        dedup._seen["msg-1"] = time.time() - 10  # 10s ago, TTL is 5s
        assert dedup.is_duplicate("msg-1") is False, \
            "Expired entry should not be treated as duplicate"

    def test_expired_entry_gets_refreshed(self):
        """After an expired entry is allowed through, it should be re-tracked."""
        dedup = MessageDeduplicator(ttl_seconds=5)
        assert dedup.is_duplicate("msg-1") is False

        # Expire the entry
        dedup._seen["msg-1"] = time.time() - 10

        # Should be allowed through (expired)
        assert dedup.is_duplicate("msg-1") is False
        # Now should be duplicate again (freshly tracked)
        assert dedup.is_duplicate("msg-1") is True

    def test_different_messages_not_confused(self):
        """Different message IDs are independent."""
        dedup = MessageDeduplicator(ttl_seconds=60)
        assert dedup.is_duplicate("msg-1") is False
        assert dedup.is_duplicate("msg-2") is False
        assert dedup.is_duplicate("msg-1") is True
        assert dedup.is_duplicate("msg-2") is True

    def test_empty_id_never_duplicate(self):
        """Empty/None message IDs are never treated as duplicate."""
        dedup = MessageDeduplicator(ttl_seconds=60)
        assert dedup.is_duplicate("") is False
        assert dedup.is_duplicate("") is False

    def test_max_size_eviction_prunes_expired(self):
        """Cache pruning on overflow removes expired entries."""
        dedup = MessageDeduplicator(max_size=5, ttl_seconds=60)
        # Add 6 entries, with the first 3 expired
        now = time.time()
        for i in range(3):
            dedup._seen[f"old-{i}"] = now - 120  # expired (2 min ago, TTL 60s)
        for i in range(3):
            dedup.is_duplicate(f"new-{i}")
        # Now we have 6 entries. Next insert triggers pruning.
        dedup.is_duplicate("trigger")
        # The 3 expired entries should be gone, leaving 4 fresh ones
        assert len(dedup._seen) == 4
        assert "old-0" not in dedup._seen
        assert "new-0" in dedup._seen

    def test_max_size_eviction_caps_fresh_entries(self):
        """Fresh entries must still be capped to max_size on overflow."""
        dedup = MessageDeduplicator(max_size=2, ttl_seconds=60)

        dedup.is_duplicate("msg-1")
        dedup.is_duplicate("msg-2")
        dedup.is_duplicate("msg-3")

        assert len(dedup._seen) == 2
        assert "msg-1" not in dedup._seen
        assert "msg-2" in dedup._seen
        assert "msg-3" in dedup._seen

    def test_ttl_zero_means_no_dedup(self):
        """With TTL=0, all entries expire immediately."""
        dedup = MessageDeduplicator(ttl_seconds=0)
        assert dedup.is_duplicate("msg-1") is False
        # Entry was just added at time.time(), and TTL is 0,
        # so now - seen_time >= 0 = ttl, meaning it's expired
        # But time.time() might be the exact same float, so
        # the check is `now - ts < ttl` which is `0 < 0` = False
        # This means TTL=0 effectively disables dedup
        assert dedup.is_duplicate("msg-1") is False
