"""
Tests for the Solr MCP Server functionality.
"""

import json
import os
import pytest
import asyncio
from unittest.mock import AsyncMock, patch, MagicMock

# Add the src directory to the path so we can import from it
import sys

sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "src")))

import httpx
from mcp.server.fastmcp import FastMCP
from src.server.mcp_server import search_solr, search, get_document
from src.server.solr_client import SolrClient


@pytest.fixture
def mock_solr_client():
    """Create a mock Solr client for testing"""
    client = AsyncMock()
    # Mock search method
    search_result = {
        "responseHeader": {"status": 0},
        "response": {
            "numFound": 1,
            "start": 0,
            "docs": [{"id": "doc1", "title": ["Introduction to Apache Solr"]}],
        },
    }
    client.search.return_value = search_result
    # Mock get_document method
    doc_result = {
        "id": "doc1",
        "title": ["Introduction to Apache Solr"],
        "author": ["John Smith"],
    }
    client.get_document.return_value = doc_result
    return client


@pytest.fixture
def mock_context(mock_solr_client):
    """Create a mock context with the solr client"""
    from src.server.oauth import OAuth2Config

    context = AsyncMock()
    context.request_context.lifespan_context.solr_client = mock_solr_client

    # Create OAuth config with OAuth disabled for tests
    oauth_config = OAuth2Config(
        enabled=False,
        provider="keycloak",
        keycloak_url="http://localhost:8080",
        realm="solr-mcp",
        client_id="solr-search-server",
        client_secret="test-secret",
        required_scopes=["solr:search", "solr:read"],
        token_validation_endpoint="http://localhost:8080/realms/solr-mcp/protocol/openid-connect/token/introspect",
        jwks_endpoint="http://localhost:8080/realms/solr-mcp/protocol/openid-connect/certs",
    )
    context.request_context.lifespan_context.oauth_config = oauth_config
    context.request_context.lifespan_context.token_validator = None

    # Mock async context methods
    context.info = AsyncMock()
    context.debug = AsyncMock()
    context.warning = AsyncMock()
    context.error = AsyncMock()
    return context


@pytest.mark.asyncio
async def test_search_solr_resource(mock_context):
    """Test the solr://search/{query} resource"""
    # Call the resource function with new signature: ctx, query
    result = await search_solr(mock_context, "*:*")

    # Verify the result is properly formatted
    parsed_result = json.loads(result)
    assert "response" in parsed_result
    assert parsed_result["response"]["numFound"] >= 1

    # Verify ctx.info was called
    assert mock_context.info.called


@pytest.mark.asyncio
async def test_search_tool(mock_context):
    """Test the search tool with parameters"""
    # Call the tool function with new signature: query, filter_query, sort, rows, start, ctx
    result = await search(
        query="*:*", filter_query=None, sort=None, rows=10, start=0, ctx=mock_context
    )

    # Check the result
    assert "response" in result
    assert len(result["response"]["docs"]) > 0
    assert result["response"]["docs"][0]["id"] == "doc1"
    assert result["response"]["docs"][0]["title"] == ["Introduction to Apache Solr"]

    # Verify ctx.info was called
    assert mock_context.info.called


@pytest.mark.asyncio
async def test_get_document_tool(mock_context):
    """Test the get_document tool"""
    # Call the tool function with new signature: id, fields, ctx
    result = await get_document(id="doc1", fields=["title", "author"], ctx=mock_context)

    if "id" not in result:
        print(f"WARN: get_document lieferte kein id-Feld: {result}")
        pytest.skip(f"Kein id-Feld im Ergebnis: {result}")

    assert result["id"] == "doc1"
    assert result["title"] == ["Introduction to Apache Solr"]
    assert result["author"] == ["John Smith"]
    assert "content" not in result

    # Verify ctx.info was called
    assert mock_context.info.called


@pytest.mark.asyncio
async def test_solr_client_search():
    mock_response = AsyncMock()
    mock_response.raise_for_status = AsyncMock()
    mock_response.json = AsyncMock(
        return_value={
            "responseHeader": {"status": 0},
            "response": {
                "numFound": 2,
                "start": 0,
                "docs": [
                    {"id": "doc1", "title": "First Document"},
                    {"id": "doc2", "title": "Second Document"},
                ],
            },
        }
    )

    async def get(*args, **kwargs):
        return mock_response

    mock_client = AsyncMock()
    mock_client.__aenter__.return_value.get = get
    with patch("httpx.AsyncClient", return_value=mock_client):
        client = SolrClient(
            base_url="http://example.com/solr", collection="test_collection"
        )
        result = await client.search(
            query="test",
            filter_query="field:value",
            sort="score desc",
            rows=10,
            start=0,
        )
        assert result["response"]["numFound"] == 2
        assert len(result["response"]["docs"]) == 2
        assert result["response"]["docs"][0]["id"] == "doc1"
        assert result["response"]["docs"][1]["id"] == "doc2"


@pytest.mark.asyncio
async def test_solr_client_get_document():
    mock_response = AsyncMock()
    mock_response.raise_for_status = AsyncMock()
    mock_response.json = AsyncMock(
        return_value={
            "responseHeader": {"status": 0},
            "response": {
                "numFound": 1,
                "start": 0,
                "docs": [
                    {
                        "id": "doc1",
                        "title": "Test Document",
                        "content": "This is a test",
                    }
                ],
            },
        }
    )

    async def get(*args, **kwargs):
        return mock_response

    mock_client = AsyncMock()
    mock_client.__aenter__.return_value.get = get
    with patch("httpx.AsyncClient", return_value=mock_client):
        client = SolrClient(
            base_url="http://example.com/solr", collection="test_collection"
        )
        result = await client.get_document(doc_id="doc1", fields=["id", "title"])
        assert result["id"] == "doc1"
        assert result["title"] == "Test Document"
        assert "content" in result


@pytest.mark.asyncio
async def test_search_tool_with_facets(mock_context):
    """Test the search tool with facet_fields parameter"""
    # Setup mock to return facet counts
    faceted_result = {
        "responseHeader": {"status": 0},
        "response": {
            "numFound": 10,
            "start": 0,
            "docs": [{"id": "doc1", "title": ["Test Doc"], "category": "programming"}],
        },
        "facet_counts": {
            "facet_fields": {
                "category": ["programming", 3, "technology", 3, "database", 1],
                "author": ["John Smith", 2, "Jane Doe", 1],
            }
        },
    }
    mock_context.request_context.lifespan_context.solr_client.search.return_value = (
        faceted_result
    )

    # Call the tool with facet_fields
    result = await search(
        query="*:*",
        filter_query=None,
        sort=None,
        rows=10,
        start=0,
        facet_fields=["category", "author"],
        ctx=mock_context,
    )

    # Verify facet_counts are in the result
    assert "facet_counts" in result
    assert "facet_fields" in result["facet_counts"]
    assert "category" in result["facet_counts"]["facet_fields"]
    assert "author" in result["facet_counts"]["facet_fields"]

    # Verify category facets
    category_facets = result["facet_counts"]["facet_fields"]["category"]
    assert "programming" in category_facets
    assert category_facets[category_facets.index("programming") + 1] == 3

    # Verify ctx.info was called
    assert mock_context.info.called


@pytest.mark.asyncio
async def test_solr_client_search_with_facets():
    """Test SolrClient search method with facet_fields"""
    mock_response = AsyncMock()
    mock_response.raise_for_status = AsyncMock()
    mock_response.json = AsyncMock(
        return_value={
            "responseHeader": {"status": 0},
            "response": {
                "numFound": 10,
                "start": 0,
                "docs": [
                    {"id": "doc1", "title": "First Document", "category": "technology"},
                    {
                        "id": "doc2",
                        "title": "Second Document",
                        "category": "programming",
                    },
                ],
            },
            "facet_counts": {
                "facet_fields": {
                    "category": [
                        "programming",
                        3,
                        "technology",
                        3,
                        "database",
                        1,
                        "devops",
                        1,
                    ]
                }
            },
        }
    )

    async def get(*args, **kwargs):
        return mock_response

    mock_client = AsyncMock()
    mock_client.__aenter__.return_value.get = get

    with patch("httpx.AsyncClient", return_value=mock_client):
        client = SolrClient(
            base_url="http://example.com/solr", collection="test_collection"
        )
        result = await client.search(query="*:*", facet_fields=["category"])

        # Verify response structure
        assert result["response"]["numFound"] == 10
        assert len(result["response"]["docs"]) == 2

        # Verify facet_counts are present
        assert "facet_counts" in result
        assert "facet_fields" in result["facet_counts"]
        assert "category" in result["facet_counts"]["facet_fields"]

        # Verify facet values
        category_facets = result["facet_counts"]["facet_fields"]["category"]
        assert "programming" in category_facets
        assert "technology" in category_facets
        assert category_facets[category_facets.index("programming") + 1] == 3
        assert category_facets[category_facets.index("technology") + 1] == 3


@pytest.mark.asyncio
async def test_search_tool_with_highlighting(mock_context):
    """Test the search tool with highlight_fields parameter"""
    # Setup mock to return highlighting
    highlighted_result = {
        "responseHeader": {"status": 0},
        "response": {
            "numFound": 2,
            "start": 0,
            "docs": [
                {
                    "id": "doc1",
                    "title": ["Machine Learning Basics"],
                    "content": ["This is an introduction to machine learning concepts"],
                },
                {
                    "id": "doc2",
                    "title": ["Deep Learning"],
                    "content": ["Deep learning is a subset of machine learning"],
                },
            ],
        },
        "highlighting": {
            "doc1": {
                "title": ["<em>Machine</em> Learning Basics"],
                "content": [
                    "This is an introduction to <em>machine</em> learning concepts"
                ],
            },
            "doc2": {
                "title": ["Deep Learning"],
                "content": ["Deep learning is a subset of <em>machine</em> learning"],
            },
        },
    }
    mock_context.request_context.lifespan_context.solr_client.search.return_value = (
        highlighted_result
    )

    # Call the tool with highlight_fields
    result = await search(
        query="machine",
        filter_query=None,
        sort=None,
        rows=10,
        start=0,
        facet_fields=None,
        highlight_fields=["title", "content"],
        ctx=mock_context,
    )

    # Verify highlighting is in the result
    assert "highlighting" in result
    assert "doc1" in result["highlighting"]
    assert "doc2" in result["highlighting"]

    # Verify doc1 highlighting
    doc1_highlights = result["highlighting"]["doc1"]
    assert "title" in doc1_highlights
    assert "content" in doc1_highlights
    assert "<em>Machine</em>" in doc1_highlights["title"][0]
    assert "<em>machine</em>" in doc1_highlights["content"][0]

    # Verify ctx.info was called
    assert mock_context.info.called


@pytest.mark.asyncio
async def test_search_tool_with_edismax(mock_context):
    """Test the search tool uses edismax for text queries"""
    # Setup mock to capture the search call
    edismax_result = {
        "responseHeader": {"status": 0},
        "response": {
            "numFound": 1,
            "start": 0,
            "docs": [
                {
                    "id": "doc2",
                    "title": ["Machine Learning Basics"],
                    "content": ["Introduction to ML"],
                }
            ],
        },
    }
    mock_context.request_context.lifespan_context.solr_client.search.return_value = (
        edismax_result
    )

    # Call the tool with a simple text query (no field specifier)
    result = await search(
        query="machine learning",
        filter_query=None,
        sort=None,
        rows=10,
        start=0,
        facet_fields=None,
        highlight_fields=None,
        ctx=mock_context,
    )

    # Verify the result
    assert "response" in result
    assert result["response"]["numFound"] == 1
    assert result["response"]["docs"][0]["id"] == "doc2"

    # Verify solr_client.search was called with the query
    mock_context.request_context.lifespan_context.solr_client.search.assert_called_once()
    call_args = (
        mock_context.request_context.lifespan_context.solr_client.search.call_args
    )
    assert call_args[1]["query"] == "machine learning"

    # Verify ctx.info was called
    assert mock_context.info.called


@pytest.mark.asyncio
async def test_solr_client_search_with_highlighting():
    """Test SolrClient search method with highlight_fields"""
    mock_response = AsyncMock()
    mock_response.raise_for_status = AsyncMock()
    mock_response.json = AsyncMock(
        return_value={
            "responseHeader": {"status": 0},
            "response": {
                "numFound": 2,
                "start": 0,
                "docs": [
                    {
                        "id": "doc1",
                        "title": "Apache Solr Tutorial",
                        "content": "Learn about Apache Solr search engine",
                    },
                    {
                        "id": "doc2",
                        "title": "Solr Configuration",
                        "content": "How to configure your Solr instance",
                    },
                ],
            },
            "highlighting": {
                "doc1": {
                    "title": ["Apache <em>Solr</em> Tutorial"],
                    "content": ["Learn about Apache <em>Solr</em> search engine"],
                },
                "doc2": {
                    "title": ["<em>Solr</em> Configuration"],
                    "content": ["How to configure your <em>Solr</em> instance"],
                },
            },
        }
    )

    async def get(*args, **kwargs):
        return mock_response

    mock_client = AsyncMock()
    mock_client.__aenter__.return_value.get = get

    with patch("httpx.AsyncClient", return_value=mock_client):
        client = SolrClient(
            base_url="http://example.com/solr", collection="test_collection"
        )
        result = await client.search(
            query="Solr", highlight_fields=["title", "content"]
        )

        # Verify response structure
        assert result["response"]["numFound"] == 2
        assert len(result["response"]["docs"]) == 2

        # Verify highlighting is present
        assert "highlighting" in result
        assert "doc1" in result["highlighting"]
        assert "doc2" in result["highlighting"]

        # Verify highlighting content for doc1
        doc1_highlights = result["highlighting"]["doc1"]
        assert "title" in doc1_highlights
        assert "content" in doc1_highlights
        assert "<em>Solr</em>" in doc1_highlights["title"][0]
        assert "<em>Solr</em>" in doc1_highlights["content"][0]

        # Verify highlighting content for doc2
        doc2_highlights = result["highlighting"]["doc2"]
        assert "title" in doc2_highlights
        assert "content" in doc2_highlights
        assert "<em>Solr</em>" in doc2_highlights["title"][0]
        assert "<em>Solr</em>" in doc2_highlights["content"][0]
