"""Unit tests for DeleteQueueOperation (Story 3.3, AC #1-7)."""

from unittest.mock import AsyncMock, MagicMock

import pytest

from rabbitmq_mcp_connection.operations.topology import DeleteQueueOperation
from rabbitmq_mcp_connection.schemas.topology import DeleteQueueRequest


class TestDeleteQueueOperation:
    """Test DeleteQueueOperation execution (Story 3.3, AC #1-7)."""

    @pytest.fixture
    def mock_http_client(self):
        """Create mock HTTPClientManager for tests."""
        client = MagicMock()
        client.request = AsyncMock()
        return client

    @pytest.fixture
    def operation(self, mock_http_client):
        """Create DeleteQueueOperation instance with mock client."""
        return DeleteQueueOperation(http_client=mock_http_client)

    @pytest.mark.asyncio
    async def test_delete_empty_queue_without_force(self, operation, mock_http_client):
        """Test deleting empty queue without force flag succeeds (AC #3)."""
        # Mock GET response for safety check (empty queue)
        get_response = MagicMock()
        get_response.status_code = 200
        get_response.json.return_value = {"messages": 0, "consumers": 0}

        # Mock DELETE response (success)
        delete_response = MagicMock()
        delete_response.status_code = 204

        mock_http_client.request.side_effect = [get_response, delete_response]

        request = DeleteQueueRequest(name="empty-queue", vhost="/")
        result = await operation.execute(request, correlation_id="test-123")

        assert result["status"] == "success"
        assert result["message"] == "Queue 'empty-queue' deleted"

        # Verify GET and DELETE calls
        assert mock_http_client.request.call_count == 2
        get_call = mock_http_client.request.call_args_list[0]
        assert get_call.kwargs["method"] == "GET"
        assert "/api/queues/%2F/empty-queue" in get_call.kwargs["endpoint"]

        delete_call = mock_http_client.request.call_args_list[1]
        assert delete_call.kwargs["method"] == "DELETE"
        assert "/api/queues/%2F/empty-queue" in delete_call.kwargs["endpoint"]

    @pytest.mark.asyncio
    async def test_delete_queue_with_messages_blocked(
        self, operation, mock_http_client
    ):
        """Test deletion blocked when queue has messages (AC #1)."""
        # Mock GET response for safety check (queue with messages)
        get_response = MagicMock()
        get_response.status_code = 200
        get_response.json.return_value = {"messages": 100, "consumers": 0}

        mock_http_client.request.return_value = get_response

        request = DeleteQueueRequest(name="orders", vhost="/", force=False)

        with pytest.raises(ValueError) as exc_info:
            await operation.execute(request, correlation_id="test-123")

        error_msg = str(exc_info.value)
        assert "Queue 'orders' has 100 messages and 0 consumers" in error_msg
        assert "Use --force flag (CLI) or force=true parameter (API)" in error_msg

        # Verify only GET call was made (DELETE not reached)
        assert mock_http_client.request.call_count == 1

    @pytest.mark.asyncio
    async def test_delete_queue_with_consumers_blocked(
        self, operation, mock_http_client
    ):
        """Test deletion blocked when queue has consumers (AC #1)."""
        # Mock GET response for safety check (queue with consumers)
        get_response = MagicMock()
        get_response.status_code = 200
        get_response.json.return_value = {"messages": 0, "consumers": 5}

        mock_http_client.request.return_value = get_response

        request = DeleteQueueRequest(name="tasks", vhost="/", force=False)

        with pytest.raises(ValueError) as exc_info:
            await operation.execute(request, correlation_id="test-123")

        error_msg = str(exc_info.value)
        assert "Queue 'tasks' has 0 messages and 5 consumers" in error_msg
        assert "Use --force flag (CLI) or force=true parameter (API)" in error_msg

    @pytest.mark.asyncio
    async def test_delete_queue_with_messages_and_consumers_blocked(
        self, operation, mock_http_client
    ):
        """Test deletion blocked when queue has both messages and consumers (AC #1)."""
        # Mock GET response for safety check
        get_response = MagicMock()
        get_response.status_code = 200
        get_response.json.return_value = {"messages": 100, "consumers": 2}

        mock_http_client.request.return_value = get_response

        request = DeleteQueueRequest(name="orders", vhost="/", force=False)

        with pytest.raises(ValueError) as exc_info:
            await operation.execute(request, correlation_id="test-123")

        error_msg = str(exc_info.value)
        assert "Queue 'orders' has 100 messages and 2 consumers" in error_msg
        assert "Use --force flag (CLI) or force=true parameter (API)" in error_msg

    @pytest.mark.asyncio
    async def test_force_delete_bypasses_safety_check(
        self, operation, mock_http_client
    ):
        """Test force flag bypasses safety check (AC #2)."""
        # Mock DELETE response only (no GET for safety check)
        delete_response = MagicMock()
        delete_response.status_code = 204

        mock_http_client.request.return_value = delete_response

        request = DeleteQueueRequest(name="orders", vhost="/", force=True)
        result = await operation.execute(request, correlation_id="test-123")

        assert result["status"] == "success"
        assert result["message"] == "Queue 'orders' deleted"

        # Verify only DELETE call was made (no GET for safety check)
        assert mock_http_client.request.call_count == 1
        delete_call = mock_http_client.request.call_args
        assert delete_call.kwargs["method"] == "DELETE"

    @pytest.mark.asyncio
    async def test_force_delete_with_messages_succeeds(
        self, operation, mock_http_client
    ):
        """Test force deletion succeeds even with messages (AC #2)."""
        delete_response = MagicMock()
        delete_response.status_code = 204

        mock_http_client.request.return_value = delete_response

        request = DeleteQueueRequest(name="orders", vhost="/prod", force=True)
        result = await operation.execute(request, correlation_id="test-123")

        assert result["status"] == "success"
        assert result["message"] == "Queue 'orders' deleted"

    @pytest.mark.asyncio
    async def test_delete_nonexistent_queue(self, operation, mock_http_client):
        """Test deleting non-existent queue returns error (AC #5)."""
        # Mock GET response for safety check (404 Not Found)
        get_response = MagicMock()
        get_response.status_code = 404

        mock_http_client.request.return_value = get_response

        request = DeleteQueueRequest(name="nonexistent", vhost="/")

        with pytest.raises(ValueError) as exc_info:
            await operation.execute(request, correlation_id="test-123")

        error_msg = str(exc_info.value)
        assert "Queue 'nonexistent' not found in vhost '/'" in error_msg

    @pytest.mark.asyncio
    async def test_delete_race_condition_404_after_get(
        self, operation, mock_http_client
    ):
        """Test race condition: queue deleted between GET and DELETE (Task 4)."""
        # Mock GET response (queue exists)
        get_response = MagicMock()
        get_response.status_code = 200
        get_response.json.return_value = {"messages": 0, "consumers": 0}

        # Mock DELETE response (404 - queue already deleted)
        delete_response = MagicMock()
        delete_response.status_code = 404

        mock_http_client.request.side_effect = [get_response, delete_response]

        request = DeleteQueueRequest(name="temp-queue", vhost="/")
        result = await operation.execute(request, correlation_id="test-123")

        # Should treat as success (queue is already deleted)
        assert result["status"] == "success"
        assert result["message"] == "Queue 'temp-queue' deleted"

    @pytest.mark.asyncio
    async def test_delete_with_custom_vhost(self, operation, mock_http_client):
        """Test deletion from custom vhost with URL encoding."""
        get_response = MagicMock()
        get_response.status_code = 200
        get_response.json.return_value = {"messages": 0, "consumers": 0}

        delete_response = MagicMock()
        delete_response.status_code = 204

        mock_http_client.request.side_effect = [get_response, delete_response]

        request = DeleteQueueRequest(name="tasks", vhost="/prod")
        result = await operation.execute(request, correlation_id="test-123")

        assert result["status"] == "success"

        # Verify vhost was URL-encoded
        delete_call = mock_http_client.request.call_args_list[1]
        assert "%2Fprod" in delete_call.kwargs["endpoint"]

    @pytest.mark.asyncio
    async def test_delete_with_special_chars_in_name(self, operation, mock_http_client):
        """Test deletion with special characters in queue name (URL encoding)."""
        get_response = MagicMock()
        get_response.status_code = 200
        get_response.json.return_value = {"messages": 0, "consumers": 0}

        delete_response = MagicMock()
        delete_response.status_code = 204

        mock_http_client.request.side_effect = [get_response, delete_response]

        request = DeleteQueueRequest(name="my-queue.name", vhost="/")
        result = await operation.execute(request, correlation_id="test-123")

        assert result["status"] == "success"

        # Verify queue name was URL-encoded
        delete_call = mock_http_client.request.call_args_list[1]
        assert "my-queue.name" in delete_call.kwargs["endpoint"]

    @pytest.mark.asyncio
    async def test_delete_success_response_format(self, operation, mock_http_client):
        """Test successful deletion returns proper response format (AC #4)."""
        get_response = MagicMock()
        get_response.status_code = 200
        get_response.json.return_value = {"messages": 0, "consumers": 0}

        delete_response = MagicMock()
        delete_response.status_code = 204

        mock_http_client.request.side_effect = [get_response, delete_response]

        request = DeleteQueueRequest(name="test-queue", vhost="/")
        result = await operation.execute(request)

        assert isinstance(result, dict)
        assert result["status"] == "success"
        assert "message" in result
        assert "test-queue" in result["message"]


class TestDeleteQueueSafetyCheck:
    """Test safety check logic in detail (Story 3.3, AC #1, #3)."""

    @pytest.fixture
    def mock_http_client(self):
        """Create mock HTTPClientManager for tests."""
        client = MagicMock()
        client.request = AsyncMock()
        return client

    @pytest.fixture
    def operation(self, mock_http_client):
        """Create DeleteQueueOperation instance with mock client."""
        return DeleteQueueOperation(http_client=mock_http_client)

    @pytest.mark.parametrize(
        "messages,consumers,should_block",
        [
            (0, 0, False),  # Empty queue - allow
            (1, 0, True),  # Has messages - block
            (0, 1, True),  # Has consumers - block
            (1, 1, True),  # Has both - block
            (100, 0, True),  # Many messages - block
            (0, 5, True),  # Many consumers - block
            (100, 2, True),  # Many messages and consumers - block
        ],
    )
    @pytest.mark.asyncio
    async def test_safety_check_scenarios(
        self, operation, mock_http_client, messages, consumers, should_block
    ):
        """Test various safety check scenarios (AC #1, #3)."""
        get_response = MagicMock()
        get_response.status_code = 200
        get_response.json.return_value = {"messages": messages, "consumers": consumers}

        delete_response = MagicMock()
        delete_response.status_code = 204

        mock_http_client.request.side_effect = [get_response, delete_response]

        request = DeleteQueueRequest(name="test-queue", vhost="/", force=False)

        if should_block:
            with pytest.raises(ValueError) as exc_info:
                await operation.execute(request)

            error_msg = str(exc_info.value)
            assert f"has {messages} messages and {consumers} consumers" in error_msg
            # Only GET should be called (DELETE not reached)
            assert mock_http_client.request.call_count == 1
        else:
            result = await operation.execute(request)
            assert result["status"] == "success"
            # Both GET and DELETE should be called
            assert mock_http_client.request.call_count == 2


class TestDeleteQueueErrorHandling:
    """Test error handling scenarios (Story 3.3, Task 4)."""

    @pytest.fixture
    def mock_http_client(self):
        """Create mock HTTPClientManager for tests."""
        client = MagicMock()
        client.request = AsyncMock()
        return client

    @pytest.fixture
    def operation(self, mock_http_client):
        """Create DeleteQueueOperation instance with mock client."""
        return DeleteQueueOperation(http_client=mock_http_client)

    @pytest.mark.asyncio
    async def test_timeout_during_safety_check(self, operation, mock_http_client):
        """Test timeout exception during safety check."""
        import httpx

        mock_http_client.request.side_effect = httpx.TimeoutException("Timeout")

        request = DeleteQueueRequest(name="test-queue", vhost="/")

        with pytest.raises(httpx.TimeoutException):
            await operation.execute(request)

    @pytest.mark.asyncio
    async def test_timeout_during_delete(self, operation, mock_http_client):
        """Test timeout exception during delete operation."""
        import httpx

        get_response = MagicMock()
        get_response.status_code = 200
        get_response.json.return_value = {"messages": 0, "consumers": 0}

        mock_http_client.request.side_effect = [
            get_response,
            httpx.TimeoutException("Timeout"),
        ]

        request = DeleteQueueRequest(name="test-queue", vhost="/")

        with pytest.raises(httpx.TimeoutException):
            await operation.execute(request)

    @pytest.mark.asyncio
    async def test_unexpected_status_code_in_delete(self, operation, mock_http_client):
        """Test unexpected status code in DELETE response."""
        get_response = MagicMock()
        get_response.status_code = 200
        get_response.json.return_value = {"messages": 0, "consumers": 0}

        delete_response = MagicMock()
        delete_response.status_code = 500  # Unexpected error

        mock_http_client.request.side_effect = [get_response, delete_response]

        request = DeleteQueueRequest(name="test-queue", vhost="/")

        with pytest.raises(ValueError) as exc_info:
            await operation.execute(request)

        assert "Unexpected response" in str(exc_info.value)

    @pytest.mark.asyncio
    async def test_unexpected_status_code_in_safety_check(
        self, operation, mock_http_client
    ):
        """Test unexpected status code in GET (safety check) response."""
        get_response = MagicMock()
        get_response.status_code = 500  # Server error

        mock_http_client.request.return_value = get_response

        request = DeleteQueueRequest(name="test-queue", vhost="/")

        with pytest.raises(ValueError) as exc_info:
            await operation.execute(request)

        assert "Failed to retrieve queue info" in str(exc_info.value)


class TestDeleteQueueCorrelationId:
    """Test correlation ID propagation (Story 3.3, AC #7, ADR-009)."""

    @pytest.fixture
    def mock_http_client(self):
        """Create mock HTTPClientManager for tests."""
        client = MagicMock()
        client.request = AsyncMock()
        return client

    @pytest.fixture
    def operation(self, mock_http_client):
        """Create DeleteQueueOperation instance with mock client."""
        return DeleteQueueOperation(http_client=mock_http_client)

    @pytest.mark.asyncio
    async def test_correlation_id_passed_through(self, operation, mock_http_client):
        """Test correlation ID is accepted and logged (AC #7)."""
        get_response = MagicMock()
        get_response.status_code = 200
        get_response.json.return_value = {"messages": 0, "consumers": 0}

        delete_response = MagicMock()
        delete_response.status_code = 204

        mock_http_client.request.side_effect = [get_response, delete_response]

        request = DeleteQueueRequest(name="test-queue", vhost="/")
        result = await operation.execute(request, correlation_id="test-correlation-123")

        assert result["status"] == "success"

    @pytest.mark.asyncio
    async def test_correlation_id_optional(self, operation, mock_http_client):
        """Test operation works without correlation ID."""
        get_response = MagicMock()
        get_response.status_code = 200
        get_response.json.return_value = {"messages": 0, "consumers": 0}

        delete_response = MagicMock()
        delete_response.status_code = 204

        mock_http_client.request.side_effect = [get_response, delete_response]

        request = DeleteQueueRequest(name="test-queue", vhost="/")
        result = await operation.execute(request)

        assert result["status"] == "success"
