# allow: no-sut-import — black-box HTTP test; drives real routes through the Flask test client
"""
Tests for chat research triggering edge cases.

These tests verify the complex research triggering logic in the send_message endpoint.
"""

import json
from unittest.mock import patch, MagicMock


class TestResearchTriggering:
    """Tests for research triggering in send_message endpoint."""

    def test_send_message_trigger_research_false_skips_research(
        self, authenticated_client
    ):
        """Test that trigger_research=false skips research process."""
        # Create a session first
        create_response = authenticated_client.post(
            "/api/chat/sessions",
            json={"initial_query": "Test query"},
            content_type="application/json",
        )
        session_id = json.loads(create_response.data)["session_id"]

        # Send message with trigger_research=False
        response = authenticated_client.post(
            f"/api/chat/sessions/{session_id}/messages",
            json={"content": "Test message", "trigger_research": False},
            content_type="application/json",
        )

        assert response.status_code == 200
        data = json.loads(response.data)
        assert data["success"] is True
        assert data["research_id"] is None
        assert data["research_mode"] == "none"

    def test_send_message_trigger_research_true_returns_research_id(
        self, authenticated_client
    ):
        """Test that trigger_research=true starts research and returns research_id."""
        # Create a session first
        create_response = authenticated_client.post(
            "/api/chat/sessions",
            json={"initial_query": "Test query"},
            content_type="application/json",
        )
        session_id = json.loads(create_response.data)["session_id"]

        # Mock the research process to avoid actually running it
        # Patch at the source module where the function is defined
        with patch(
            "local_deep_research.chat.routes.start_research_process"
        ) as mock_start_research:
            response = authenticated_client.post(
                f"/api/chat/sessions/{session_id}/messages",
                json={
                    "content": "What is quantum computing?",
                    "trigger_research": True,
                },
                content_type="application/json",
            )

            assert response.status_code == 200
            data = json.loads(response.data)
            assert data["success"] is True
            assert data["research_id"] is not None
            assert data["research_mode"] == "quick"
            # Verify research process was started
            assert mock_start_research.called

    def test_send_message_blocks_research_when_password_missing_encrypted(
        self, authenticated_client
    ):
        """Encrypted DB + no DB password -> 401, no research spawned (#4457).

        Mirrors the guard on /start_research and the follow-up route: a
        chat-triggered research must not run passwordless, because its
        background metric writes (token/search) would be silently dropped,
        leaving the metrics dashboard empty while the research completes.
        Trigger in production: session-password TTL expiry or a server
        restart while the session cookie is still valid.
        """
        create_response = authenticated_client.post(
            "/api/chat/sessions",
            json={"initial_query": "Test query"},
            content_type="application/json",
        )
        session_id = json.loads(create_response.data)["session_id"]

        # The route delegates the encryption-aware decision to the shared
        # resolve_user_password helper; simulate "encrypted DB, password
        # lost" -> (None, session_expired=True).
        with (
            patch(
                "local_deep_research.chat.routes.resolve_user_password",
                return_value=(None, True),
            ),
            patch(
                "local_deep_research.chat.routes.start_research_process"
            ) as mock_start_research,
        ):
            response = authenticated_client.post(
                f"/api/chat/sessions/{session_id}/messages",
                json={
                    "content": "What is quantum computing?",
                    "trigger_research": True,
                },
                content_type="application/json",
            )

        assert response.status_code == 401
        data = json.loads(response.data)
        assert data["success"] is False
        assert "log out" in data["error"].lower()
        # No worker thread spawned — the run is refused before side effects.
        mock_start_research.assert_not_called()

    def test_send_message_unencrypted_db_allows_research_without_password(
        self, authenticated_client
    ):
        """A plaintext (no-SQLCipher) DB must NOT be blocked by the password guard.

        resolve_user_password returns (None, session_expired=False) for an
        unencrypted install, so the chat research path must proceed.
        """
        create_response = authenticated_client.post(
            "/api/chat/sessions",
            json={"initial_query": "Test query"},
            content_type="application/json",
        )
        session_id = json.loads(create_response.data)["session_id"]

        with (
            patch(
                "local_deep_research.chat.routes.resolve_user_password",
                return_value=(None, False),
            ),
            patch(
                "local_deep_research.chat.routes.start_research_process"
            ) as mock_start_research,
        ):
            response = authenticated_client.post(
                f"/api/chat/sessions/{session_id}/messages",
                json={
                    "content": "What is quantum computing?",
                    "trigger_research": True,
                },
                content_type="application/json",
            )

        assert response.status_code == 200
        assert mock_start_research.called

    def test_send_message_uses_quick_mode_by_default(
        self, authenticated_client
    ):
        """Test that research always uses 'quick' mode in chat."""
        # Create a session
        create_response = authenticated_client.post(
            "/api/chat/sessions",
            json={"initial_query": "Test query"},
            content_type="application/json",
        )
        session_id = json.loads(create_response.data)["session_id"]

        # Mock research process at source module
        with patch("local_deep_research.chat.routes.start_research_process"):
            response = authenticated_client.post(
                f"/api/chat/sessions/{session_id}/messages",
                json={
                    "content": "Research question",
                    "trigger_research": True,
                    "research_mode": "detailed",  # Try to request detailed
                },
                content_type="application/json",
            )

            # Even when requesting "detailed", chat uses "quick"
            assert response.status_code == 200
            data = json.loads(response.data)
            assert data["research_mode"] == "quick"

    def test_send_message_nonexistent_session_returns_404(
        self, authenticated_client
    ):
        """Test that sending message to non-existent session returns 404."""
        response = authenticated_client.post(
            "/api/chat/sessions/non-existent-session-id-12345/messages",
            json={"content": "Test message", "trigger_research": False},
            content_type="application/json",
        )

        assert response.status_code == 404
        data = json.loads(response.data)
        assert data["success"] is False
        assert "Session not found" in data["error"]

    def test_send_message_context_includes_history(self, authenticated_client):
        """Test that research context includes conversation history."""
        # Create a session
        create_response = authenticated_client.post(
            "/api/chat/sessions",
            json={"initial_query": "Initial topic"},
            content_type="application/json",
        )
        session_id = json.loads(create_response.data)["session_id"]

        # Send first message without research
        authenticated_client.post(
            f"/api/chat/sessions/{session_id}/messages",
            json={"content": "What is AI?", "trigger_research": False},
            content_type="application/json",
        )

        # Send second message with research - verify context is passed
        # Patch at correct locations - ChatContextManager is imported at module level in routes
        with patch("local_deep_research.chat.routes.start_research_process"):
            with patch(
                "local_deep_research.chat.routes.ChatContextManager"
            ) as mock_context_manager_class:
                mock_context_manager = MagicMock()
                mock_context_manager.build_research_context.return_value = {
                    "session_id": session_id,
                    "is_multi_turn": True,
                }
                mock_context_manager_class.return_value = mock_context_manager

                response = authenticated_client.post(
                    f"/api/chat/sessions/{session_id}/messages",
                    json={
                        "content": "Tell me more about machine learning",
                        "trigger_research": True,
                    },
                    content_type="application/json",
                )

                assert response.status_code == 200
                # Verify context manager was called with messages
                assert mock_context_manager_class.called

    def test_send_message_default_trigger_research_is_true(
        self, authenticated_client
    ):
        """Test that trigger_research defaults to True when not specified."""
        # Create a session
        create_response = authenticated_client.post(
            "/api/chat/sessions",
            json={"initial_query": "Test query"},
            content_type="application/json",
        )
        session_id = json.loads(create_response.data)["session_id"]

        # Mock research process at source module
        with patch(
            "local_deep_research.chat.routes.start_research_process"
        ) as mock_start_research:
            response = authenticated_client.post(
                f"/api/chat/sessions/{session_id}/messages",
                json={
                    "content": "Test message"
                },  # No trigger_research specified
                content_type="application/json",
            )

            assert response.status_code == 200
            data = json.loads(response.data)
            # Should trigger research by default
            assert data["research_id"] is not None
            assert mock_start_research.called

    def test_follow_up_uses_contextual_followup_strategy(
        self, authenticated_client
    ):
        """Test that follow-up messages switch to enhanced-contextual-followup strategy."""
        create_response = authenticated_client.post(
            "/api/chat/sessions",
            json={"initial_query": "Initial topic"},
            content_type="application/json",
        )
        session_id = json.loads(create_response.data)["session_id"]

        # Add first message without research
        authenticated_client.post(
            f"/api/chat/sessions/{session_id}/messages",
            json={"content": "What is AI?", "trigger_research": False},
            content_type="application/json",
        )

        # Send follow-up — mock context to return is_multi_turn=True
        with patch(
            "local_deep_research.chat.routes.start_research_process"
        ) as mock_start:
            with patch(
                "local_deep_research.chat.routes.ChatContextManager"
            ) as mock_ctx_cls:
                mock_ctx = MagicMock()
                mock_ctx.build_research_context.return_value = {
                    "session_id": session_id,
                    "is_multi_turn": True,
                }
                mock_ctx_cls.return_value = mock_ctx

                authenticated_client.post(
                    f"/api/chat/sessions/{session_id}/messages",
                    json={"content": "Tell me more", "trigger_research": True},
                    content_type="application/json",
                )

                assert mock_start.called
                call_kwargs = mock_start.call_args[1]
                assert call_kwargs["strategy"] == "enhanced-contextual-followup"
                assert (
                    call_kwargs["research_context"]["delegate_strategy"]
                    is not None
                )

    def test_first_message_uses_user_strategy(self, authenticated_client):
        """Test that first message uses the user's configured strategy, not followup."""
        create_response = authenticated_client.post(
            "/api/chat/sessions",
            json={"initial_query": "First question"},
            content_type="application/json",
        )
        session_id = json.loads(create_response.data)["session_id"]

        with patch(
            "local_deep_research.chat.routes.start_research_process"
        ) as mock_start:
            with patch(
                "local_deep_research.chat.routes.ChatContextManager"
            ) as mock_ctx_cls:
                mock_ctx = MagicMock()
                mock_ctx.build_research_context.return_value = {
                    "session_id": session_id,
                    "is_multi_turn": False,
                }
                mock_ctx_cls.return_value = mock_ctx

                authenticated_client.post(
                    f"/api/chat/sessions/{session_id}/messages",
                    json={"content": "First query", "trigger_research": True},
                    content_type="application/json",
                )

                assert mock_start.called
                call_kwargs = mock_start.call_args[1]
                assert call_kwargs["strategy"] != "enhanced-contextual-followup"
