import pytest
from unittest.mock import patch


@pytest.fixture(autouse=True)
def mock_registry_client():
    with patch("mca_sdk.registry.client.RegistryClient._fetch_model_config_internal") as mock_fetch:
        # Prevent TypeError: '>=' not supported between instances of 'MagicMock' and 'int'
        from mca_sdk.registry.models import ModelConfig

        mock_fetch.return_value = ModelConfig(
            model_id="test",
            model_version="1.0",
            service_name="test-service",
            team_name="test-team",
            model_category="internal",
            model_type="regression",
        )
        yield mock_fetch


import pytest
from unittest.mock import Mock

from mca_sdk.resilience.dead_letter_queue import DeadLetterQueue
from mca_sdk.buffering.retry import RetryPolicy


class TestRetryWithDLQ:
    """Test RetryPolicy integration with DeadLetterQueue."""

    def test_failed_item_enqueued_to_dlq(self):
        """Test that a failed item is enqueued to the DLQ after all retries."""
        # Arrange
        dlq = Mock(spec=DeadLetterQueue)
        retry_policy = RetryPolicy(max_attempts=3, dlq=dlq)
        export_function = Mock(side_effect=Exception("Export failed"))
        export_function.__name__ = "export_function"
        item_to_export = {"data": "test"}

        # Act
        with pytest.raises(Exception, match="Export failed"):
            retry_policy.execute(export_function, item_to_export)

        # Assert
        assert export_function.call_count == 3

        # Verify DLQ enqueue was called with structured item
        dlq.enqueue.assert_called_once()
        call_args = dlq.enqueue.call_args

        # Check item structure
        assert "item" in call_args.kwargs
        item = call_args.kwargs["item"]
        assert isinstance(item, dict)
        assert item["function"] == "export_function"
        assert item["args"] == (item_to_export,)
        assert item["kwargs"] == {}

        # Check error and retry_count
        assert call_args.kwargs["retry_count"] == 3
        assert isinstance(call_args.kwargs["error"], Exception)

    def test_no_dlq_configured(self):
        """Test that no error occurs if DLQ is not configured."""
        # Arrange
        retry_policy = RetryPolicy(max_attempts=3, dlq=None)
        export_function = Mock(side_effect=Exception("Export failed"))
        item_to_export = {"data": "test"}

        # Act
        with pytest.raises(Exception, match="Export failed"):
            retry_policy.execute(export_function, item_to_export)

        # Assert
        assert export_function.call_count == 3

    def test_dlq_captures_args_and_kwargs(self):
        """Test that DLQ captures both positional and keyword arguments."""
        # Arrange
        dlq = Mock(spec=DeadLetterQueue)
        retry_policy = RetryPolicy(max_attempts=2, dlq=dlq)
        export_function = Mock(side_effect=Exception("Export failed"))
        export_function.__name__ = "export_with_options"

        # Act - call with both args and kwargs
        with pytest.raises(Exception, match="Export failed"):
            retry_policy.execute(
                export_function, "arg1", "arg2", option1="value1", option2="value2"
            )

        # Assert
        dlq.enqueue.assert_called_once()
        call_args = dlq.enqueue.call_args
        item = call_args.kwargs["item"]

        # Verify all arguments captured
        assert item["function"] == "export_with_options"
        assert item["args"] == ("arg1", "arg2")
        assert item["kwargs"] == {"option1": "value1", "option2": "value2"}

    def test_dlq_handles_function_without_name(self):
        """Test DLQ handles functions without __name__ attribute (e.g., lambdas)."""
        # Arrange
        dlq = Mock(spec=DeadLetterQueue)
        retry_policy = RetryPolicy(max_attempts=2, dlq=dlq)

        # Lambda has no __name__ in same way
        export_lambda = lambda x: (_ for _ in ()).throw(Exception("Lambda failed"))

        # Act
        with pytest.raises(Exception):
            retry_policy.execute(export_lambda, "test_data")

        # Assert - should not crash, should capture function as string
        dlq.enqueue.assert_called_once()
        call_args = dlq.enqueue.call_args
        item = call_args.kwargs["item"]

        # Should have some string representation
        assert "function" in item
        assert isinstance(item["function"], str)
        assert item["args"] == ("test_data",)
