"""
Copyright (C) 2025 Luciano Guerche

This file is part of rabbitmq-mcp-server.

rabbitmq-mcp-server is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

rabbitmq-mcp-server is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with rabbitmq-mcp-server. If not, see <https://www.gnu.org/licenses/>.
"""

"""Integration tests for DeleteExchangeOperation (Story 3.7, Task 9)."""

from collections.abc import Iterator
from contextlib import contextmanager

import pytest

pytest.importorskip("testcontainers.rabbitmq")
from testcontainers.rabbitmq import RabbitMqContainer

from rabbitmq_mcp_connection.http_client import HTTPClientManager
from rabbitmq_mcp_connection.operations.topology import (
    CreateExchangeOperation,
    DeleteExchangeOperation,
)
from rabbitmq_mcp_connection.schemas.topology import (
    CreateExchangeRequest,
    DeleteExchangeRequest,
)


@contextmanager
def rabbitmq_container() -> Iterator[RabbitMqContainer]:
    """Create RabbitMQ testcontainer."""
    container = RabbitMqContainer("rabbitmq:3.13-management").with_exposed_ports(
        5672, 15672
    )
    with container:
        yield container


@contextmanager
def http_client(container: RabbitMqContainer) -> Iterator[HTTPClientManager]:
    """Create HTTPClientManager for testcontainer."""
    host = container.get_container_host_ip()
    port = int(container.get_exposed_port(15672))

    client = HTTPClientManager(
        host=host,
        port=port,
        username="guest",
        password="guest",
        use_tls=False,
        timeout=10.0,
    )
    try:
        yield client
    finally:
        pass


@pytest.mark.integration
@pytest.mark.asyncio
async def test_delete_exchange_without_bindings_succeeds():
    """Test AC #3: Exchange without bindings deletes without requiring force flag."""
    with rabbitmq_container() as container:
        with http_client(container) as client:
            # Create exchange
            create_op = CreateExchangeOperation(client)
            create_req = CreateExchangeRequest(
                vhost="/",
                name="test.integration.no.bindings",
                type="direct",
                durable=False,
            )
            await create_op.execute(create_req, correlation_id="test-1")

            # Delete without force (should succeed)
            delete_op = DeleteExchangeOperation(client)
            delete_req = DeleteExchangeRequest(
                vhost="/",
                name="test.integration.no.bindings",
                force=False,
            )
            response = await delete_op.execute(delete_req, correlation_id="test-1")

            assert response.status == "success"
            assert "deleted" in response.message.lower()


@pytest.mark.integration
@pytest.mark.asyncio
async def test_delete_exchange_with_bindings_blocked():
    """Test AC #1: Exchange with active bindings blocked without force flag."""
    with rabbitmq_container() as container:
        with http_client(container) as client:
            # Create exchange
            create_exchange = CreateExchangeOperation(client)
            exchange_req = CreateExchangeRequest(
                vhost="/",
                name="test.integration.with.bindings",
                type="direct",
                durable=False,
            )
            await create_exchange.execute(exchange_req, correlation_id="test-2")

            # Create binding manually via HTTP API
            import urllib.parse

            encoded_vhost = urllib.parse.quote("/", safe="")
            encoded_queue = urllib.parse.quote("test.queue.bound", safe="")
            encoded_exchange = urllib.parse.quote(
                "test.integration.with.bindings", safe=""
            )

            # Create queue first
            await client.request(
                "PUT",
                f"/api/queues/{encoded_vhost}/{encoded_queue}",
                json={"auto_delete": False, "durable": False},
            )

            # Create binding
            await client.request(
                "POST",
                f"/api/bindings/{encoded_vhost}/e/{encoded_exchange}/q/{encoded_queue}",
                json={"routing_key": "test.key", "arguments": {}},
            )

            # Try to delete without force (should fail)
            delete_op = DeleteExchangeOperation(client)
            delete_req = DeleteExchangeRequest(
                vhost="/",
                name="test.integration.with.bindings",
                force=False,
            )

            with pytest.raises(ValueError) as exc:
                await delete_op.execute(delete_req, correlation_id="test-2")

            assert "active bindings" in str(exc.value).lower()
            assert "force=true" in str(exc.value).lower()


@pytest.mark.integration
@pytest.mark.asyncio
async def test_delete_exchange_with_force_bypasses_bindings():
    """Test AC #2: force=true allows deletion despite active bindings."""
    with rabbitmq_container() as container:
        with http_client(container) as client:
            # Create exchange
            create_exchange = CreateExchangeOperation(client)
            exchange_req = CreateExchangeRequest(
                vhost="/",
                name="test.integration.force.delete",
                type="direct",
                durable=False,
            )
            await create_exchange.execute(exchange_req, correlation_id="test-3")

            # Create binding
            import urllib.parse

            encoded_vhost = urllib.parse.quote("/", safe="")
            encoded_queue = urllib.parse.quote("test.queue.forced", safe="")
            encoded_exchange = urllib.parse.quote(
                "test.integration.force.delete", safe=""
            )

            await client.request(
                "PUT",
                f"/api/queues/{encoded_vhost}/{encoded_queue}",
                json={"auto_delete": False, "durable": False},
            )
            await client.request(
                "POST",
                f"/api/bindings/{encoded_vhost}/e/{encoded_exchange}/q/{encoded_queue}",
                json={"routing_key": "test.key", "arguments": {}},
            )

            # Delete with force=true (should succeed)
            delete_op = DeleteExchangeOperation(client)
            delete_req = DeleteExchangeRequest(
                vhost="/",
                name="test.integration.force.delete",
                force=True,
            )
            response = await delete_op.execute(delete_req, correlation_id="test-3")

            assert response.status == "success"
            assert "deleted" in response.message.lower()


@pytest.mark.integration
@pytest.mark.asyncio
async def test_delete_exchange_not_found_idempotent():
    """Test that deleting non-existent exchange returns success (idempotent)."""
    with rabbitmq_container() as container:
        with http_client(container) as client:
            delete_op = DeleteExchangeOperation(client)
            delete_req = DeleteExchangeRequest(
                vhost="/",
                name="test.integration.does.not.exist",
                force=False,
            )

            # Should not raise, returns success
            response = await delete_op.execute(delete_req, correlation_id="test-4")
            assert response.status == "success"


@pytest.mark.integration
@pytest.mark.asyncio
async def test_delete_exchange_performance_under_100ms():
    """Test AC #7: Deletion completes in <100ms."""
    import time

    with rabbitmq_container() as container:
        with http_client(container) as client:
            # Create exchange
            create_op = CreateExchangeOperation(client)
            create_req = CreateExchangeRequest(
                vhost="/",
                name="test.integration.performance",
                type="direct",
                durable=False,
            )
            await create_op.execute(create_req, correlation_id="test-5")

            # Delete and measure time
            delete_op = DeleteExchangeOperation(client)
            delete_req = DeleteExchangeRequest(
                vhost="/",
                name="test.integration.performance",
                force=False,
            )

            start_time = time.time()
            await delete_op.execute(delete_req, correlation_id="test-5")
            duration_ms = (time.time() - start_time) * 1000

            # Allow some margin for container overhead (150ms)
            assert (
                duration_ms < 150
            ), f"Deletion took {duration_ms:.2f}ms (expected <150ms)"
