"""Integration tests for HealthChecker with live RabbitMQ (Story 2.4).

Tests health check functionality with real AMQP and HTTP connections using testcontainers.
"""

from __future__ import annotations

import asyncio

import pytest
from testcontainers.rabbitmq import RabbitMqContainer

from rabbitmq_mcp_connection.connection.health import HealthChecker
from rabbitmq_mcp_connection.connection.manager import ConnectionManager
from rabbitmq_mcp_connection.http_client import HTTPClientManager
from rabbitmq_mcp_connection.schemas.connection import ConnectionConfig


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


@pytest.fixture
def rabbitmq_config(rabbitmq_container):
    """Create AMQP 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.fixture
async def connection_manager(rabbitmq_config):
    """Create and connect AMQP connection manager."""
    manager = ConnectionManager(rabbitmq_config)
    await manager.connect()
    yield manager
    await manager.disconnect()


@pytest.fixture
async def http_client_manager(rabbitmq_container):
    """Create HTTP client manager for RabbitMQ Management API."""
    client = HTTPClientManager(
        host=rabbitmq_container.get_container_host_ip(),
        port=int(rabbitmq_container.get_exposed_port(15672)),
        username="guest",
        password="guest",
        use_tls=False,
        timeout=1,
    )
    yield client
    await client.close()


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

    async def test_health_check_with_live_rabbitmq(
        self, connection_manager, http_client_manager
    ):
        """Test successful health check with live RabbitMQ (AC #1, #2, #3)."""
        import time

        checker = HealthChecker(
            connection_manager,
            timeout=1.0,
            http_client=http_client_manager,
        )

        start = time.perf_counter()
        status = await checker.check_health()
        duration = time.perf_counter() - start

        # AC #3: Health checks complete in <1 second
        assert duration < 1.0
        assert status.latency_ms < 1000

        # AC #1: AMQP connection health verified
        assert status.amqp_connected is True
        assert status.is_connected is True
        assert status.broker_available is True

        # AC #2: HTTP API health verified via GET /api/healthchecks/node
        assert status.http_connected is True

        # AC #7: Health status exposed with all fields
        assert status.is_healthy is True
        assert status.consecutive_failures == 0
        assert status.last_check is not None
        assert status.error_message is None

    async def test_amqp_failure_detection(
        self, connection_manager, http_client_manager
    ):
        """Test AMQP failure detection within 30s (AC #6)."""
        checker = HealthChecker(
            connection_manager,
            timeout=1.0,
            http_client=http_client_manager,
        )

        # Initial check should pass
        status1 = await checker.check_health()
        assert status1.amqp_connected is True

        # Disconnect AMQP
        await connection_manager.disconnect()

        # Check should detect failure
        status2 = await checker.check_health()
        assert status2.amqp_connected is False
        assert status2.is_healthy is False

    async def test_background_health_checks_run_periodically(
        self, connection_manager, http_client_manager
    ):
        """Test health checks run every 30 seconds (AC #4)."""
        # Use short interval for testing
        checker = HealthChecker(
            connection_manager,
            timeout=1.0,
            http_client=http_client_manager,
            check_interval=1,  # 1 second for fast testing
        )

        await checker.start()

        # Wait for multiple checks
        await asyncio.sleep(2.5)

        # Should have run at least 2 checks
        assert checker._successful_checks >= 2

        # Get latest status
        status = checker.get_status()
        assert status is not None
        assert status.is_healthy is True

        await checker.stop()

    async def test_consecutive_failure_trigger(
        self, connection_manager, http_client_manager
    ):
        """Test consecutive failures (3+) trigger reconnection (AC #6)."""
        checker = HealthChecker(
            connection_manager,
            timeout=1.0,
            http_client=http_client_manager,
            check_interval=0.5,  # Fast checks
        )

        # Start checks
        await checker.start()
        await asyncio.sleep(0.6)

        # Verify healthy initially
        status1 = checker.get_status()
        assert status1.is_healthy is True
        assert status1.consecutive_failures == 0

        # Disconnect to cause failures
        await connection_manager.disconnect()

        # Wait for 3+ failure checks (0.5s * 3 = 1.5s + buffer)
        await asyncio.sleep(2.0)

        # Should have accumulated failures
        status2 = checker.get_status()
        assert status2.consecutive_failures >= 3
        assert status2.is_healthy is False

        await checker.stop()

    async def test_configurable_check_interval(
        self, connection_manager, http_client_manager
    ):
        """Test health check interval is configurable (AC #4)."""
        # Set interval to 2 seconds
        checker = HealthChecker(
            connection_manager,
            timeout=1.0,
            http_client=http_client_manager,
            check_interval=2,
        )

        await checker.start()

        # Wait 3 seconds
        await asyncio.sleep(3.0)

        # Should have completed ~1-2 checks (not 3+)
        assert checker._successful_checks <= 2

        await checker.stop()

    async def test_health_status_via_mcp_tool_format(
        self, connection_manager, http_client_manager
    ):
        """Test health status returns valid JSON for MCP tool (AC #8)."""
        checker = HealthChecker(
            connection_manager,
            timeout=1.0,
            http_client=http_client_manager,
        )

        status = await checker.check_health()

        # Should serialize to JSON
        json_data = status.model_dump_json()
        assert isinstance(json_data, str)
        assert "amqp_connected" in json_data
        assert "http_connected" in json_data
        assert "last_check" in json_data

    async def test_http_only_failure(self, connection_manager, http_client_manager):
        """Test HTTP failure scenario while AMQP remains connected."""
        checker = HealthChecker(
            connection_manager,
            timeout=1.0,
            http_client=http_client_manager,
        )

        # Initial check - both healthy
        status1 = await checker.check_health()
        assert status1.is_healthy is True

        # Close HTTP client to simulate HTTP failure
        await http_client_manager.close()

        # Check should detect HTTP failure
        status2 = await checker.check_health()
        assert status2.amqp_connected is True
        assert status2.http_connected is False
        assert status2.is_healthy is False

    async def test_health_check_timeout_enforcement(
        self, connection_manager, http_client_manager
    ):
        """Test health checks complete within configured timeout (AC #3)."""
        import time

        checker = HealthChecker(
            connection_manager,
            timeout=1.0,
            http_client=http_client_manager,
        )

        start = time.perf_counter()
        status = await checker.check_health()
        duration = time.perf_counter() - start

        # Must complete within timeout
        assert duration < 1.5  # Allow small overhead
        assert status.latency_ms < 1000

    async def test_graceful_shutdown_during_checks(
        self, connection_manager, http_client_manager
    ):
        """Test graceful shutdown while checks are running."""
        checker = HealthChecker(
            connection_manager,
            timeout=1.0,
            http_client=http_client_manager,
            check_interval=5,  # Long interval
        )

        await checker.start()

        # Stop immediately (during first check or sleep)
        await checker.stop()

        # Should complete cleanly
        assert checker._running is False
        assert checker._background_task is None

    async def test_health_status_after_recovery(
        self, connection_manager, http_client_manager
    ):
        """Test consecutive_failures resets after recovery (AC #6)."""
        checker = HealthChecker(
            connection_manager,
            timeout=1.0,
            http_client=http_client_manager,
            check_interval=0.5,
        )

        await checker.start()

        # Let it run healthy checks
        await asyncio.sleep(0.6)

        # Disconnect
        await connection_manager.disconnect()
        await asyncio.sleep(1.5)  # Accumulate failures

        # Verify failures
        status_failed = checker.get_status()
        assert status_failed.consecutive_failures >= 2

        # Reconnect
        await connection_manager.connect()
        await asyncio.sleep(1.0)  # Wait for recovery check

        # Should reset counter
        status_recovered = checker.get_status()
        assert status_recovered.consecutive_failures == 0
        assert status_recovered.is_healthy is True

        await checker.stop()

    async def test_metrics_tracking(self, connection_manager, http_client_manager):
        """Test health check metrics are tracked (AC #5)."""
        checker = HealthChecker(
            connection_manager,
            timeout=1.0,
            http_client=http_client_manager,
            check_interval=0.3,
        )

        await checker.start()
        await asyncio.sleep(1.0)

        # Should have metrics
        assert checker._successful_checks > 0
        assert checker._total_check_duration > 0.0

        await checker.stop()
