"""Unit tests for HTTPClientManager TLS integration."""

from __future__ import annotations

import ssl
from unittest.mock import AsyncMock, MagicMock, patch

import httpx
import pytest

from rabbitmq_mcp_connection.connection.exceptions import (
    SSLCertificateVerificationError,
)
from rabbitmq_mcp_connection.http_client import HTTPClientManager


class TestHTTPClientManagerTLS:
    """Test HTTPClientManager TLS integration (AC #2, #6, #7, #8)."""

    def test_init_with_tls_enabled_creates_https_base_url(self):
        """Test that HTTPS base URL is created when TLS enabled (AC #2)."""
        with patch(
            "rabbitmq_mcp_connection.http_client.TLSContextManager.create_ssl_context"
        ) as mock_create_ssl:
            mock_create_ssl.return_value = MagicMock(spec=ssl.SSLContext)

            client = HTTPClientManager(
                host="localhost",
                port=15672,
                username="guest",
                password="guest",
                use_tls=True,
                verify_cert=True,
            )

            assert client.base_url.startswith("https://")
            assert "localhost:15672" in client.base_url

    def test_init_with_tls_disabled_creates_http_base_url(self):
        """Test that HTTP base URL is created when TLS disabled."""
        client = HTTPClientManager(
            host="localhost",
            port=15672,
            username="guest",
            password="guest",
            use_tls=False,
        )

        assert client.base_url.startswith("http://")
        assert "localhost:15672" in client.base_url

    def test_init_with_tls_enabled_creates_ssl_context(self):
        """Test that SSL context is created when TLS enabled (AC #2)."""
        with patch(
            "rabbitmq_mcp_connection.http_client.TLSContextManager.create_ssl_context"
        ) as mock_create_ssl:
            mock_ssl_context = MagicMock(spec=ssl.SSLContext)
            mock_create_ssl.return_value = mock_ssl_context

            HTTPClientManager(
                host="localhost",
                port=15672,
                username="guest",
                password="guest",
                use_tls=True,
                verify_cert=True,
                ca_bundle=None,
            )

            # Verify SSL context was created with correct parameters
            mock_create_ssl.assert_called_once_with(
                verify_cert=True,
                ca_bundle=None,
            )

    def test_init_with_tls_disabled_logs_warning(self, caplog):
        """Test that warning is logged when TLS disabled (AC #8)."""
        import logging

        caplog.set_level(logging.WARNING)

        HTTPClientManager(
            host="localhost",
            port=15672,
            username="guest",
            password="guest",
            use_tls=False,
        )

        # Verify TLS disabled warning was logged
        assert len(caplog.records) > 0

    def test_init_with_verify_cert_false_logs_warning(self, caplog):
        """Test that warning is logged when verification disabled (AC #4, #8)."""
        import logging

        caplog.set_level(logging.WARNING)

        with patch(
            "rabbitmq_mcp_connection.http_client.TLSContextManager.create_ssl_context"
        ) as mock_create_ssl:
            mock_create_ssl.return_value = MagicMock(spec=ssl.SSLContext)

            HTTPClientManager(
                host="localhost",
                port=15672,
                username="guest",
                password="guest",
                use_tls=True,
                verify_cert=False,
            )

            # Verify certificate verification disabled warning was logged
            assert len(caplog.records) > 0

    def test_init_with_custom_ca_bundle(self, tmp_path):
        """Test initialization with custom CA bundle (AC #5)."""
        ca_file = tmp_path / "ca.pem"
        ca_file.write_text("FAKE CA CERT")

        with patch(
            "rabbitmq_mcp_connection.http_client.TLSContextManager.create_ssl_context"
        ) as mock_create_ssl:
            mock_create_ssl.return_value = MagicMock(spec=ssl.SSLContext)

            HTTPClientManager(
                host="localhost",
                port=15672,
                username="guest",
                password="guest",
                use_tls=True,
                verify_cert=True,
                ca_bundle=str(ca_file),
            )

            # Verify CA bundle was passed to SSL context creation
            mock_create_ssl.assert_called_once_with(
                verify_cert=True,
                ca_bundle=str(ca_file),
            )

    @pytest.mark.asyncio
    async def test_request_ssl_certificate_verification_failed(self):
        """Test SSL certificate verification failure during request (AC #6)."""
        with patch(
            "rabbitmq_mcp_connection.http_client.TLSContextManager.create_ssl_context"
        ) as mock_create_ssl:
            mock_create_ssl.return_value = MagicMock(spec=ssl.SSLContext)

            client = HTTPClientManager(
                host="localhost",
                port=15672,
                username="guest",
                password="guest",
                use_tls=True,
                verify_cert=True,
            )

            # Mock httpx.AsyncClient.request to raise SSL error
            with patch.object(
                client.client,
                "request",
                side_effect=httpx.TransportError(
                    "certificate verify failed: certificate has expired"
                ),
            ):
                with pytest.raises(SSLCertificateVerificationError) as exc_info:
                    await client.request("GET", "/overview")

                # Verify error message contains details
                assert "certificate" in str(exc_info.value).lower()

    @pytest.mark.asyncio
    async def test_request_ssl_expired_certificate(self):
        """Test expired certificate detection during request (AC #6)."""
        with patch(
            "rabbitmq_mcp_connection.http_client.TLSContextManager.create_ssl_context"
        ) as mock_create_ssl:
            mock_create_ssl.return_value = MagicMock(spec=ssl.SSLContext)

            client = HTTPClientManager(
                host="localhost",
                port=15672,
                username="guest",
                password="guest",
                use_tls=True,
                verify_cert=True,
            )

            with patch.object(
                client.client,
                "request",
                side_effect=httpx.TransportError(
                    "certificate verify failed: certificate has expired"
                ),
            ):
                with pytest.raises(SSLCertificateVerificationError) as exc_info:
                    await client.request("GET", "/overview")

                # Verify error details mention expiration
                assert exc_info.value.details is not None
                assert "expired" in exc_info.value.details.lower()

    @pytest.mark.asyncio
    async def test_request_ssl_hostname_mismatch(self):
        """Test hostname mismatch detection during request (AC #6)."""
        with patch(
            "rabbitmq_mcp_connection.http_client.TLSContextManager.create_ssl_context"
        ) as mock_create_ssl:
            mock_create_ssl.return_value = MagicMock(spec=ssl.SSLContext)

            client = HTTPClientManager(
                host="localhost",
                port=15672,
                username="guest",
                password="guest",
                use_tls=True,
                verify_cert=True,
            )

            with patch.object(
                client.client,
                "request",
                side_effect=httpx.TransportError(
                    "certificate verify failed: hostname mismatch"
                ),
            ):
                with pytest.raises(SSLCertificateVerificationError) as exc_info:
                    await client.request("GET", "/overview")

                # Verify error details mention hostname
                assert exc_info.value.details is not None
                details_lower = exc_info.value.details.lower()
                assert "hostname" in details_lower or "mismatch" in details_lower

    @pytest.mark.asyncio
    async def test_request_ssl_untrusted_ca(self):
        """Test untrusted CA detection during request (AC #6)."""
        with patch(
            "rabbitmq_mcp_connection.http_client.TLSContextManager.create_ssl_context"
        ) as mock_create_ssl:
            mock_create_ssl.return_value = MagicMock(spec=ssl.SSLContext)

            client = HTTPClientManager(
                host="localhost",
                port=15672,
                username="guest",
                password="guest",
                use_tls=True,
                verify_cert=True,
            )

            with patch.object(
                client.client,
                "request",
                side_effect=httpx.TransportError(
                    "certificate verify failed: self signed certificate"
                ),
            ):
                with pytest.raises(SSLCertificateVerificationError) as exc_info:
                    await client.request("GET", "/overview")

                # Verify error details mention self-signed or untrusted
                assert exc_info.value.details is not None
                details_lower = exc_info.value.details.lower()
                assert "self" in details_lower or "untrusted" in details_lower

    @pytest.mark.asyncio
    async def test_request_ssl_error_evicts_connections(self):
        """Test that SSL errors trigger connection pool eviction (AC #8)."""
        with patch(
            "rabbitmq_mcp_connection.http_client.TLSContextManager.create_ssl_context"
        ) as mock_create_ssl:
            mock_create_ssl.return_value = MagicMock(spec=ssl.SSLContext)

            client = HTTPClientManager(
                host="localhost",
                port=15672,
                username="guest",
                password="guest",
                use_tls=True,
                verify_cert=True,
            )

            with (
                patch.object(
                    client.client,
                    "request",
                    side_effect=httpx.TransportError(
                        "certificate verify failed: certificate has expired"
                    ),
                ),
                patch.object(client, "_evict_stale_connections") as mock_evict,
            ):
                with pytest.raises(SSLCertificateVerificationError):
                    await client.request("GET", "/overview")

                # Verify eviction was triggered
                mock_evict.assert_called_once()
