"""
Tests für Tool Gateway Service.
"""

import pytest
from unittest.mock import AsyncMock, patch, MagicMock
import httpx

from llm_gateway.services.tool_gateway import (
    ToolGateway,
    ToolGatewayConfig,
    SearchDepth,
    SearchResult,
    SearchResponse,
    TavilyError,
    ToolGatewayError,
    get_tool_gateway,
)
from llm_gateway.services.pii_detector import PIIDetector, RedactionResult


class TestToolGatewayConfig:
    """Tests für ToolGatewayConfig."""

    def test_default_config(self):
        """Test Standardkonfiguration."""
        config = ToolGatewayConfig()

        assert config.tavily_api_key is None
        assert config.tavily_base_url == "https://api.tavily.com"
        assert config.timeout == 30
        assert config.max_results == 5
        assert config.search_depth == SearchDepth.BASIC
        assert config.include_answer is True
        assert config.pii_redaction_enabled is True

    def test_config_from_env(self):
        """Test Konfiguration aus Umgebungsvariablen."""
        with patch.dict("os.environ", {
            "TAVILY_API_KEY": "test-key",
            "TAVILY_BASE_URL": "https://custom.tavily.com",
            "TAVILY_TIMEOUT": "60",
            "TAVILY_MAX_RESULTS": "10",
            "TAVILY_SEARCH_DEPTH": "advanced",
            "PII_REDACTION_ENABLED": "false",
        }):
            config = ToolGatewayConfig.from_env()

            assert config.tavily_api_key == "test-key"
            assert config.tavily_base_url == "https://custom.tavily.com"
            assert config.timeout == 60
            assert config.max_results == 10
            assert config.search_depth == SearchDepth.ADVANCED
            assert config.pii_redaction_enabled is False


class TestToolGatewayAvailability:
    """Tests für Gateway-Verfügbarkeit."""

    def test_tavily_not_available_without_key(self):
        """Test Tavily nicht verfügbar ohne API Key."""
        config = ToolGatewayConfig(tavily_api_key=None)
        gateway = ToolGateway(config=config)

        assert gateway.tavily_available is False

    def test_tavily_available_with_key(self):
        """Test Tavily verfügbar mit API Key."""
        config = ToolGatewayConfig(tavily_api_key="test-key")
        gateway = ToolGateway(config=config)

        assert gateway.tavily_available is True


class TestToolGatewayPIIRedaction:
    """Tests für PII-Redaktion im Gateway."""

    def test_redact_query_with_email(self):
        """Test Redaktion von E-Mail in Query."""
        config = ToolGatewayConfig(pii_redaction_enabled=True)
        gateway = ToolGateway(config=config)

        result = gateway._redact_query("Kontakt test@example.com Datenschutz")

        assert result.pii_found is True
        assert "test@example.com" not in result.redacted_text
        assert "[EMAIL_REDACTED]" in result.redacted_text

    def test_no_redaction_when_disabled(self):
        """Test keine Redaktion wenn deaktiviert."""
        config = ToolGatewayConfig(pii_redaction_enabled=False)
        gateway = ToolGateway(config=config)

        result = gateway._redact_query("test@example.com")

        assert result.pii_found is False
        assert result.redacted_text == "test@example.com"


class TestToolGatewaySearch:
    """Tests für Suche mit Gateway."""

    @pytest.mark.asyncio
    async def test_search_raises_error_without_key(self):
        """Test Fehler bei Suche ohne API Key."""
        config = ToolGatewayConfig(tavily_api_key=None)
        gateway = ToolGateway(config=config)

        with pytest.raises(ToolGatewayError, match="not configured"):
            await gateway.search("test query")

    @pytest.mark.asyncio
    async def test_search_success(self):
        """Test erfolgreiche Suche."""
        config = ToolGatewayConfig(tavily_api_key="test-key")
        gateway = ToolGateway(config=config)

        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "results": [
                {
                    "title": "Test Result",
                    "url": "https://example.com",
                    "content": "Test content",
                    "score": 0.95,
                }
            ],
            "answer": "Test answer",
        }

        with patch.object(gateway, "_get_client") as mock_client:
            mock_http = AsyncMock()
            mock_http.post.return_value = mock_response
            mock_client.return_value = mock_http

            result = await gateway.search("Schulrecht Bayern")

            assert result.query == "Schulrecht Bayern"
            assert len(result.results) == 1
            assert result.results[0].title == "Test Result"
            assert result.answer == "Test answer"

    @pytest.mark.asyncio
    async def test_search_with_pii_redaction(self):
        """Test Suche mit PII-Redaktion."""
        config = ToolGatewayConfig(
            tavily_api_key="test-key",
            pii_redaction_enabled=True,
        )
        gateway = ToolGateway(config=config)

        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "results": [],
            "answer": None,
        }

        with patch.object(gateway, "_get_client") as mock_client:
            mock_http = AsyncMock()
            mock_http.post.return_value = mock_response
            mock_client.return_value = mock_http

            result = await gateway.search("Kontakt test@example.com Datenschutz")

            assert result.pii_detected is True
            assert "email" in result.pii_types
            assert result.redacted_query is not None
            assert "test@example.com" not in result.redacted_query

            # Prüfen dass redaktierte Query an Tavily gesendet wurde
            call_args = mock_http.post.call_args
            sent_query = call_args.kwargs["json"]["query"]
            assert "test@example.com" not in sent_query

    @pytest.mark.asyncio
    async def test_search_http_error(self):
        """Test HTTP-Fehler bei Suche."""
        config = ToolGatewayConfig(tavily_api_key="test-key")
        gateway = ToolGateway(config=config)

        mock_response = MagicMock()
        mock_response.status_code = 429
        mock_response.text = "Rate limit exceeded"
        mock_response.raise_for_status.side_effect = httpx.HTTPStatusError(
            "Rate limit",
            request=MagicMock(),
            response=mock_response,
        )

        with patch.object(gateway, "_get_client") as mock_client:
            mock_http = AsyncMock()
            mock_http.post.return_value = mock_response
            mock_client.return_value = mock_http

            with pytest.raises(TavilyError, match="429"):
                await gateway.search("test")

    @pytest.mark.asyncio
    async def test_search_with_domain_filters(self):
        """Test Suche mit Domain-Filtern."""
        config = ToolGatewayConfig(tavily_api_key="test-key")
        gateway = ToolGateway(config=config)

        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"results": [], "answer": None}

        with patch.object(gateway, "_get_client") as mock_client:
            mock_http = AsyncMock()
            mock_http.post.return_value = mock_response
            mock_client.return_value = mock_http

            await gateway.search(
                "test",
                include_domains=["gov.de", "schule.de"],
                exclude_domains=["wikipedia.org"],
            )

            call_args = mock_http.post.call_args
            payload = call_args.kwargs["json"]
            assert payload["include_domains"] == ["gov.de", "schule.de"]
            assert payload["exclude_domains"] == ["wikipedia.org"]


class TestSearchResult:
    """Tests für SearchResult Dataclass."""

    def test_search_result_creation(self):
        """Test SearchResult erstellen."""
        result = SearchResult(
            title="Test",
            url="https://example.com",
            content="Content",
            score=0.9,
            published_date="2024-01-15",
        )

        assert result.title == "Test"
        assert result.url == "https://example.com"
        assert result.score == 0.9

    def test_search_result_defaults(self):
        """Test SearchResult Standardwerte."""
        result = SearchResult(
            title="Test",
            url="https://example.com",
            content="Content",
        )

        assert result.score == 0.0
        assert result.published_date is None


class TestSearchResponse:
    """Tests für SearchResponse Dataclass."""

    def test_search_response_creation(self):
        """Test SearchResponse erstellen."""
        response = SearchResponse(
            query="test query",
            results=[],
            pii_detected=False,
        )

        assert response.query == "test query"
        assert len(response.results) == 0
        assert response.pii_detected is False

    def test_search_response_with_pii(self):
        """Test SearchResponse mit PII."""
        response = SearchResponse(
            query="original query",
            redacted_query="redacted query",
            results=[],
            pii_detected=True,
            pii_types=["email", "phone"],
        )

        assert response.pii_detected is True
        assert "email" in response.pii_types


class TestSearchDepthEnum:
    """Tests für SearchDepth Enum."""

    def test_search_depth_values(self):
        """Test SearchDepth Werte."""
        assert SearchDepth.BASIC.value == "basic"
        assert SearchDepth.ADVANCED.value == "advanced"


class TestGetToolGatewaySingleton:
    """Tests für Singleton Pattern."""

    def test_singleton_returns_same_instance(self):
        """Test dass get_tool_gateway Singleton zurückgibt."""
        gateway1 = get_tool_gateway()
        gateway2 = get_tool_gateway()
        assert gateway1 is gateway2


class TestToolGatewayHealthCheck:
    """Tests für Health Check."""

    @pytest.mark.asyncio
    async def test_health_check_without_tavily(self):
        """Test Health Check ohne Tavily."""
        config = ToolGatewayConfig(tavily_api_key=None)
        gateway = ToolGateway(config=config)

        status = await gateway.health_check()

        assert status["tavily"]["configured"] is False
        assert status["tavily"]["healthy"] is False

    @pytest.mark.asyncio
    async def test_health_check_with_tavily_success(self):
        """Test Health Check mit erfolgreichem Tavily."""
        config = ToolGatewayConfig(tavily_api_key="test-key")
        gateway = ToolGateway(config=config)

        with patch.object(gateway, "search") as mock_search:
            mock_search.return_value = SearchResponse(
                query="test",
                results=[],
                response_time_ms=100,
            )

            status = await gateway.health_check()

            assert status["tavily"]["configured"] is True
            assert status["tavily"]["healthy"] is True
            assert status["tavily"]["response_time_ms"] == 100

    @pytest.mark.asyncio
    async def test_health_check_with_tavily_failure(self):
        """Test Health Check mit Tavily-Fehler."""
        config = ToolGatewayConfig(tavily_api_key="test-key")
        gateway = ToolGateway(config=config)

        with patch.object(gateway, "search") as mock_search:
            mock_search.side_effect = TavilyError("Connection failed")

            status = await gateway.health_check()

            assert status["tavily"]["configured"] is True
            assert status["tavily"]["healthy"] is False
            assert "error" in status["tavily"]


class TestToolGatewayClose:
    """Tests für Gateway-Cleanup."""

    @pytest.mark.asyncio
    async def test_close_client(self):
        """Test Client-Cleanup."""
        config = ToolGatewayConfig(tavily_api_key="test-key")
        gateway = ToolGateway(config=config)

        # Simuliere dass Client erstellt wurde
        mock_client = AsyncMock()
        gateway._client = mock_client

        await gateway.close()

        mock_client.aclose.assert_called_once()
        assert gateway._client is None
