"""Phase 1 tests: RAG query endpoint.

Covers:
- POST /api/v1/query question → retrieve → LLM → bullet-point response
- Strict RAG prompt enforcement (only use retrieved context)
- Bullet-point response format
- Source metadata inclusion
"""
import pytest
from fastapi.testclient import TestClient
from unittest.mock import MagicMock, AsyncMock, patch


class TestQuery:

    @pytest.fixture
    def client(self):
        from app.main import app
        return TestClient(app)

    def test_query_returns_bullets(self, client):
        """Should return bullet-point answer with source metadata."""
        with patch("app.routers.query.QueryDecomposer") as mock_decomposer_class:
            mock_decomposer = MagicMock()
            mock_decomposer.decompose = AsyncMock(return_value=["test", "keywords"])
            mock_decomposer_class.return_value = mock_decomposer

            with patch("app.routers.query.RAGService") as mock_rag_class:
                mock_rag = MagicMock()
                mock_rag.retrieve.return_value = [
                    ("chunk one", {"filename": "test.pdf"}, 0.1),
                    ("chunk two", {"filename": "test.pdf"}, 0.2),
                ]
                mock_rag.generate_response = AsyncMock(return_value="- Bullet point answer\n- Another point")
                mock_rag_class.return_value = mock_rag

                with patch("app.routers.query.RelevanceFilter") as mock_filter_class:
                    mock_filter = MagicMock()
                    mock_filter.filter = AsyncMock(return_value=[
                        ("chunk one", {"filename": "test.pdf"}),
                        ("chunk two", {"filename": "test.pdf"}),
                    ])
                    mock_filter_class.return_value = mock_filter

                    response = client.post(
                        "/api/v1/query",
                        json={"question": "What is this about?"},
                    )

        assert response.status_code == 200
        data = response.json()
        assert "keywords" in data
        assert data["keywords"] == ["test", "keywords"]
        assert "answer" in data
        assert "- Bullet point answer" in data["answer"]
        assert "sources" in data
        assert len(data["sources"]) == 2
        assert data["sources"][0]["filename"] == "test.pdf"

    def test_query_no_relevant_chunks(self, client):
        """Should handle case when no relevant chunks found."""
        with patch("app.routers.query.QueryDecomposer") as mock_decomposer_class:
            mock_decomposer = MagicMock()
            mock_decomposer.decompose = AsyncMock(return_value=["test"])
            mock_decomposer_class.return_value = mock_decomposer

            with patch("app.routers.query.RAGService") as mock_rag_class:
                mock_rag = MagicMock()
                mock_rag.retrieve.return_value = [
                    ("chunk one", {"filename": "test.pdf"}, 0.1),
                ]
                mock_rag.generate_response = AsyncMock(return_value="I could not find any relevant information.")
                mock_rag_class.return_value = mock_rag

                with patch("app.routers.query.RelevanceFilter") as mock_filter_class:
                    mock_filter = MagicMock()
                    mock_filter.filter = AsyncMock(return_value=[])
                    mock_filter_class.return_value = mock_filter

                    response = client.post(
                        "/api/v1/query",
                        json={"question": "What is this about?"},
                    )

        assert response.status_code == 200
        data = response.json()
        assert data["keywords"] == ["test"]
        assert "could not find" in data["answer"].lower()
        assert data["sources"] == []

    def test_query_no_question(self, client):
        """Should reject request without question."""
        response = client.post("/api/v1/query", json={})

        assert response.status_code == 422
