"""Integration tests for AMQP connection with RabbitMQ."""

from __future__ import annotations

import pytest
from testcontainers.rabbitmq import RabbitMqContainer

from rabbitmq_mcp_connection.connection.exceptions import (
    AuthenticationError,
    ConnectionTimeoutError,
)
from rabbitmq_mcp_connection.connection.manager import ConnectionManager
from rabbitmq_mcp_connection.schemas.connection import ConnectionConfig, ConnectionState


@pytest.fixture(scope="module")
def rabbitmq_container():
    """Start RabbitMQ container for integration tests."""
    container = RabbitMqContainer("rabbitmq:3.12-management")
    container.start()
    yield container
    container.stop()


@pytest.fixture
def rabbitmq_config(rabbitmq_container):
    """Create connection config for RabbitMQ container."""
    return ConnectionConfig(
        host=rabbitmq_container.get_container_host_ip(),
        port=int(rabbitmq_container.get_exposed_port(5672)),
        user="guest",
        password="guest",
        vhost="/",
        timeout=30,
        heartbeat=60,
    )


@pytest.mark.asyncio
@pytest.mark.integration
class TestAMQPConnectionIntegration:
    """Integration tests with real RabbitMQ instance."""

    async def test_connect_to_rabbitmq(self, rabbitmq_config):
        """Test successful connection to RabbitMQ within 5 seconds."""
        import time

        manager = ConnectionManager(rabbitmq_config)

        start_time = time.time()
        await manager.connect()
        elapsed = time.time() - start_time

        try:
            # AC #1: Connection succeeds within 5 seconds
            assert elapsed < 5.0
            assert manager.is_connected()
            assert manager.get_state() == ConnectionState.CONNECTED
        finally:
            await manager.disconnect()

    async def test_connect_with_invalid_credentials(self, rabbitmq_container):
        """Test connection with invalid credentials fails with auth error."""
        config = ConnectionConfig(
            host=rabbitmq_container.get_container_host_ip(),
            port=int(rabbitmq_container.get_exposed_port(5672)),
            user="invalid",
            password="invalid",
            vhost="/",
            timeout=10,
        )
        manager = ConnectionManager(config)

        # AC #3: Authentication with invalid credentials should fail
        with pytest.raises(AuthenticationError) as exc_info:
            await manager.connect()

        # AC #6: Error message should include host and port
        assert config.host in str(exc_info.value)
        assert str(config.port) in str(exc_info.value)

    async def test_connect_timeout(self):
        """Test connection to unavailable host times out."""
        config = ConnectionConfig(
            host="192.0.2.1",  # TEST-NET-1, guaranteed non-routable
            port=5672,
            user="guest",
            password="guest",
            vhost="/",
            timeout=5,  # Shorter timeout for test
        )
        manager = ConnectionManager(config)

        # AC #7: Connection timeout should be respected
        with pytest.raises(ConnectionTimeoutError) as exc_info:
            await manager.connect()

        # AC #6: Error message format
        assert "Failed to connect to RabbitMQ" in str(exc_info.value)
        assert config.host in str(exc_info.value)

    async def test_connect_to_non_default_vhost(self, rabbitmq_container):
        """Test connection to non-default vhost."""
        # First, create a test vhost using management API
        import httpx

        management_port = rabbitmq_container.get_exposed_port(15672)
        base_url = (
            f"http://{rabbitmq_container.get_container_host_ip()}:{management_port}"
        )

        async with httpx.AsyncClient(auth=("guest", "guest")) as client:
            # Create test vhost
            response = await client.put(f"{base_url}/api/vhosts/test-vhost")
            assert response.status_code in (201, 204)

            # Grant permissions
            response = await client.put(
                f"{base_url}/api/permissions/test-vhost/guest",
                json={
                    "configure": ".*",
                    "write": ".*",
                    "read": ".*",
                },
            )
            assert response.status_code in (201, 204)

        # AC #4: Connection targets configured vhost
        config = ConnectionConfig(
            host=rabbitmq_container.get_container_host_ip(),
            port=int(rabbitmq_container.get_exposed_port(5672)),
            user="guest",
            password="guest",
            vhost="/test-vhost",
            timeout=30,
        )
        manager = ConnectionManager(config)

        try:
            await manager.connect()
            assert manager.is_connected()
        finally:
            await manager.disconnect()

    async def test_channel_creation(self, rabbitmq_config):
        """Test channel creation after successful connection."""
        manager = ConnectionManager(rabbitmq_config)

        try:
            await manager.connect()

            # AC #2, #4: Get channel for operations
            channel = await manager.get_channel()
            assert channel is not None
            assert not channel.is_closed
        finally:
            await manager.disconnect()

    async def test_disconnect_closes_connection(self, rabbitmq_config):
        """Test disconnect properly closes connection."""
        manager = ConnectionManager(rabbitmq_config)

        await manager.connect()
        assert manager.is_connected()

        await manager.disconnect()

        # Verify connection is closed
        assert not manager.is_connected()
        assert manager.get_state() == ConnectionState.DISCONNECTED
