"""Tests for Code Review fixes in Story 3.5."""

import json
import logging
import os
import tempfile
import time
from unittest.mock import Mock, patch

import pytest

from mca_sdk.buffering.retry import RetryPolicy
from mca_sdk.resilience.dead_letter_queue import DeadLetterQueue, MAX_ITEM_SIZE_BYTES
from mca_sdk.security.encryption import EncryptionManager
from mca_sdk.utils.exceptions import BufferingError


class TestDLQReviewFixes:
    """Tests for critical fixes from code review."""

    def test_backup_files_have_timestamps(self):
        """Test that backup files include timestamps to prevent overwrites."""
        with tempfile.TemporaryDirectory() as tmp_dir:
            dlq_path = os.path.join(tmp_dir, "dlq.json")
            key = EncryptionManager.generate_key()

            # 1. Create encrypted DLQ
            dlq = DeadLetterQueue(max_size=10, persist_path=dlq_path, encryption_key=key)
            dlq.enqueue({"data": "test"}, Exception("e"), 1)

            # 2. Corrupt/Change key to force decryption failure
            wrong_key = EncryptionManager.generate_key()

            # 3. First failure
            with patch("shutil.copy2") as mock_copy:
                # Force decryption error by using wrong key
                dlq2 = DeadLetterQueue(max_size=10, persist_path=dlq_path, encryption_key=wrong_key)

                # Check backup call
                assert mock_copy.called
                args, _ = mock_copy.call_args
                src, dst = args
                assert src == dlq_path
                assert ".failed." in dst
                # Verify timestamp format roughly (YYYYMMDD-HHMMSS)
                # Just check it contains digits and dashes
                assert any(c.isdigit() for c in dst)

                first_backup_name = dst

            # 4. Wait a second to ensure timestamp change
            time.sleep(1.1)

            # 5. Second failure
            with patch("shutil.copy2") as mock_copy:
                dlq3 = DeadLetterQueue(max_size=10, persist_path=dlq_path, encryption_key=wrong_key)

                assert mock_copy.called
                args, _ = mock_copy.call_args
                _, dst = args

                # Should be different filename
                assert dst != first_backup_name

    def test_retry_policy_reraises_buffering_error(self):
        """Test that RetryPolicy explicitly re-raises BufferingError from DLQ."""
        dlq = Mock(spec=DeadLetterQueue)
        # Simulate "Fail Fast" behavior
        dlq.enqueue.side_effect = BufferingError("Persistence failed 5 times")

        retry = RetryPolicy(max_attempts=1, dlq=dlq)

        # Should raise BufferingError, NOT swallow it
        with pytest.raises(BufferingError, match="Persistence failed 5 times"):
            retry.execute(lambda: 1 / 0)  # Trigger failure

    def test_memory_safety_pre_check(self):
        """Test that huge items are rejected/truncated BEFORE full serialization cost."""
        with tempfile.TemporaryDirectory() as tmp_dir:
            dlq = DeadLetterQueue(max_size=10, persist_path=os.path.join(tmp_dir, "dlq.json"))

            # Create a fake "huge" object that is small in memory but reports huge len() if stringified?
            # Actually, the check is `estimated_size > MAX * 2`.
            # Let's just use a string that is slightly larger than 2x MAX

            limit = MAX_ITEM_SIZE_BYTES
            huge_string = "x" * (limit * 2 + 100)

            # This should trigger the "Early size check failed" path
            dlq.enqueue(huge_string, Exception("e"), 1)

            items = dlq.inspect(1)
            assert items[0]["item"]["__truncated__"] is True
            assert items[0]["item"]["__reason__"] == "Early size check failed"

            # Verify we didn't store the huge string
            assert "x" * 100 not in str(items[0]["item"])

    def test_memory_safety_dict_estimation(self):
        """Test estimation logic for dictionaries."""
        with tempfile.TemporaryDirectory() as tmp_dir:
            dlq = DeadLetterQueue(max_size=10, persist_path=os.path.join(tmp_dir, "dlq.json"))

            limit = MAX_ITEM_SIZE_BYTES
            # Create dict that sums to > limit * 2
            # Key = 10 chars. Value = limit * 2
            huge_dict = {"key": "y" * (limit * 2 + 100)}

            dlq.enqueue(huge_dict, Exception("e"), 1)

            items = dlq.inspect(1)
            assert items[0]["item"]["__truncated__"] is True
            assert items[0]["item"]["__reason__"] == "Early size check failed"
