import pytest

from r2r import Message, R2RException, SearchMode


# Semantic Search Tests
def test_semantic_search_with_near_duplicates(client):
    """Test semantic search can handle and differentiate near-duplicate content"""
    # Create two similar but distinct documents
    doc1 = client.documents.create(
        raw_text="Aristotle was a Greek philosopher who studied logic."
    )["results"]["document_id"]
    doc2 = client.documents.create(
        raw_text="Aristotle, the Greek philosopher, studied formal logic."
    )["results"]["document_id"]

    resp = client.retrieval.search(
        query="Tell me about Aristotle's work in logic",
        search_mode="custom",
        search_settings={"use_semantic_search": True, "limit": 5},
    )
    results = resp["results"]["chunk_search_results"]

    # Both documents should be returned but with different scores
    scores = [r["score"] for r in results if r["document_id"] in [doc1, doc2]]
    assert len(scores) == 2, "Expected both similar documents"
    assert (
        len(set(scores)) == 2
    ), "Expected different scores for similar documents"


def test_semantic_search_multilingual(client):
    """Test semantic search handles multilingual content"""
    # Create documents in different languages
    docs = [
        ("Aristotle was a philosopher", "English"),
        ("Aristóteles fue un filósofo", "Spanish"),
        ("アリストテレスは哲学者でした", "Japanese"),
    ]
    doc_ids = []
    for text, lang in docs:
        doc_id = client.documents.create(
            raw_text=text, metadata={"language": lang}
        )["results"]["document_id"]
        doc_ids.append(doc_id)

    # Query in different languages
    queries = [
        "Who was Aristotle?",
        "¿Quién fue Aristóteles?",
        "アリストテレスとは誰でしたか？",
    ]

    for query in queries:
        resp = client.retrieval.search(
            query=query,
            search_mode="custom",
            search_settings={
                "use_semantic_search": True,
                "limit": len(doc_ids),
            },
        )
        results = resp["results"]["chunk_search_results"]
        assert len(results) > 0, f"No results found for query: {query}"


# UNCOMMENT LATER
# # Hybrid Search Tests
# def test_hybrid_search_weight_balance(client):
#     """Test hybrid search balances semantic and full-text scores appropriately"""
#     # Create a document with high semantic relevance but low keyword match
#     semantic_doc = client.documents.create(
#         raw_text="The ancient Greek thinker who studied under Plato made significant contributions to logic."
#     )["results"]["document_id"]

#     # Create a document with high keyword match but low semantic relevance
#     keyword_doc = client.documents.create(
#         raw_text="Aristotle is a common name in certain regions. This text mentions Aristotle but is not about philosophy."
#     )["results"]["document_id"]

#     resp = client.retrieval.search(
#         query="What were Aristotle's philosophical contributions?",
#         search_mode="custom",
#         search_settings={
#             "use_hybrid_search": True,
#             "hybrid_settings": {
#                 "semantic_weight": 0.7,
#                 "full_text_weight": 0.3,
#             },
#         },
#     )
#     results = resp["results"]["chunk_search_results"]

#     # The semantic document should rank higher
#     semantic_rank = next(
#         i for i, r in enumerate(results) if r["document_id"] == semantic_doc
#     )
#     keyword_rank = next(
#         i for i, r in enumerate(results) if r["document_id"] == keyword_doc
#     )
#     assert (
#         semantic_rank < keyword_rank
#     ), "Semantic relevance should outweigh keyword matches"


# RAG Tests
def test_rag_context_window_limits(client):
    """Test RAG handles documents at or near context window limits"""
    # Create a document that approaches the context window limit
    large_text = (
        "Aristotle " * 1000
    )  # Adjust multiplier based on your context window
    doc_id = client.documents.create(raw_text=large_text)["results"][
        "document_id"
    ]

    resp = client.retrieval.rag(
        query="Summarize this text about Aristotle",
        search_settings={"filters": {"document_id": {"$eq": str(doc_id)}}},
        rag_generation_config={"max_tokens": 100},
    )
    assert "results" in resp, "RAG should handle large context gracefully"


# UNCOMMENT LATER
# def test_rag_empty_chunk_handling(client):
#     """Test RAG properly handles empty or whitespace-only chunks"""
#     doc_id = client.documents.create(chunks=["", " ", "\n", "Valid content"])[
#         "results"
#     ]["document_id"]

#     resp = client.retrieval.rag(
#         query="What is the content?",
#         search_settings={"filters": {"document_id": {"$eq": str(doc_id)}}},
#     )
#     assert "results" in resp, "RAG should handle empty chunks gracefully"


# # Agent Tests
# def test_agent_clarification_requests(client):
#     """Test agent's ability to request clarification for ambiguous queries"""
#     msg = Message(role="user", content="Compare them")
#     resp = client.retrieval.agent(
#         message=msg,
#         search_settings={"use_semantic_search": True},
#     )
#     content = resp["results"]["messages"][-1]["content"]
#     assert any(
#         phrase in content.lower()
#         for phrase in [
#             "could you clarify",
#             "who do you",
#             "what would you",
#             "please specify",
#         ]
#     ), "Agent should request clarification for ambiguous queries"


## TODO - uncomment later
# def test_agent_source_citation_consistency(client):
#     """Test agent consistently cites sources across conversation turns"""
#     conversation_id = client.conversations.create()["results"]["id"]

#     # First turn - asking about a specific topic
#     msg1 = Message(role="user", content="What did Aristotle say about ethics?")
#     resp1 = client.retrieval.agent(
#         message=msg1,
#         conversation_id=conversation_id,
#         include_title_if_available=True,
#     )

#     # Second turn - asking for more details
#     msg2 = Message(role="user", content="Can you elaborate on that point?")
#     resp2 = client.retrieval.agent(
#         message=msg2,
#         conversation_id=conversation_id,
#         include_title_if_available=True,
#     )

#     # Check that sources are consistently cited across turns
#     sources1 = _extract_sources(resp1["results"]["messages"][-1]["content"])
#     sources2 = _extract_sources(resp2["results"]["messages"][-1]["content"])
#     assert (
#         len(sources1) > 0 and len(sources2) > 0
#     ), "Both responses should cite sources"
#     assert any(
#         s in sources2 for s in sources1
#     ), "Follow-up should reference some original sources"


## TODO - uncomment later
# # Error Handling Tests
# def test_malformed_filter_handling(client):
#     """Test system properly handles malformed filter conditions"""
#     invalid_filters = [
#         {"$invalid": {"$eq": "value"}},
#         {"field": {"$unsupported": "value"}},
#         {"$and": [{"field": "incomplete_operator"}]},
#         {"$or": []},  # Empty OR condition
#         {"$and": [{}]},  # Empty filter in AND
#     ]

#     for invalid_filter in invalid_filters:
#         with pytest.raises(R2RException) as exc_info:
#             client.retrieval.search(
#                 query="test", search_settings={"filters": invalid_filter}
#             )
#         assert exc_info.value.status_code in [
#             400,
#             422,
#         ], f"Expected validation error for filter: {invalid_filter}"


## TODO - Uncomment later
# def test_concurrent_search_stability(client):
#     """Test system handles concurrent search requests properly"""
#     import asyncio

#     async def concurrent_searches():
#         tasks = []
#         for i in range(10):  # Adjust number based on system capabilities
#             task = asyncio.create_task(
#                 client.retrieval.search_async(
#                     query=f"Concurrent test query {i}", search_mode="basic"
#                 )
#             )
#             tasks.append(task)

#         results = await asyncio.gather(*tasks, return_exceptions=True)
#         return results

#     results = asyncio.run(concurrent_searches())
#     assert all(
#         not isinstance(r, Exception) for r in results
#     ), "Concurrent searches should complete without errors"


# Helper function for source extraction
def _extract_sources(content: str) -> list[str]:
    """Extract source citations from response content"""
    # This is a simplified version - implement based on your citation format
    import re

    return re.findall(r'"([^"]*)"', content)
