"""
Tests for chat route exception handling.

These tests verify that all route endpoints handle exceptions gracefully
and return proper 500 error responses with appropriate error messages.
"""

import json
from unittest.mock import patch, MagicMock
from sqlalchemy.exc import SQLAlchemyError

from local_deep_research.chat.service import ArchiveBlockedError


class TestCreateSessionExceptionHandling:
    """Tests for exception handling in create_session endpoint."""

    def test_create_session_db_failure_returns_500(self, authenticated_client):
        """Test that database failure in create_session returns 500."""
        with patch(
            "local_deep_research.chat.routes.ChatService"
        ) as mock_service_class:
            mock_service = MagicMock()
            mock_service.create_session.side_effect = SQLAlchemyError(
                "Database connection failed"
            )
            mock_service_class.return_value = mock_service

            response = authenticated_client.post(
                "/api/chat/sessions",
                json={"initial_query": "Test query"},
                content_type="application/json",
            )

            assert response.status_code == 500
            data = json.loads(response.data)
            assert data["success"] is False
            assert "error" in data

    def test_create_session_database_failure_returns_500(
        self, authenticated_client
    ):
        """Test that database failure during session creation returns 500."""
        with patch(
            "local_deep_research.chat.service.get_user_db_session"
        ) as mock_get_session:
            mock_get_session.side_effect = RuntimeError(
                "Database connection timeout"
            )

            response = authenticated_client.post(
                "/api/chat/sessions",
                json={"initial_query": "Test query"},
                content_type="application/json",
            )

            assert response.status_code == 500
            data = json.loads(response.data)
            assert data["success"] is False


class TestGetSessionExceptionHandling:
    """Tests for exception handling in get_session endpoint."""

    def test_get_session_db_timeout_returns_500(self, authenticated_client):
        """Test that database timeout in get_session returns 500."""
        with patch(
            "local_deep_research.chat.routes.ChatService"
        ) as mock_service_class:
            mock_service = MagicMock()
            mock_service.get_session.side_effect = SQLAlchemyError(
                "Database query timeout"
            )
            mock_service_class.return_value = mock_service

            response = authenticated_client.get(
                "/api/chat/sessions/some-session-id"
            )

            assert response.status_code == 500
            data = json.loads(response.data)
            assert data["success"] is False
            assert "Failed to get chat session" in data["error"]


class TestListSessionsExceptionHandling:
    """Tests for exception handling in list_sessions endpoint."""

    def test_list_sessions_db_connection_error_returns_500(
        self, authenticated_client
    ):
        """Test that database connection error in list_sessions returns 500."""
        with patch(
            "local_deep_research.chat.routes.ChatService"
        ) as mock_service_class:
            mock_service = MagicMock()
            mock_service.list_sessions.side_effect = SQLAlchemyError(
                "Connection refused"
            )
            mock_service_class.return_value = mock_service

            response = authenticated_client.get("/api/chat/sessions")

            assert response.status_code == 500
            data = json.loads(response.data)
            assert data["success"] is False
            assert "Failed to list chat sessions" in data["error"]


class TestUpdateSessionExceptionHandling:
    """Tests for exception handling in update_session endpoint."""

    def test_update_session_db_write_failure_returns_500(
        self, authenticated_client
    ):
        """Test that database write failure in update_session returns 500."""
        with patch(
            "local_deep_research.chat.routes.ChatService"
        ) as mock_service_class:
            mock_service = MagicMock()
            mock_service.update_session_title.side_effect = SQLAlchemyError(
                "Write failed"
            )
            mock_service_class.return_value = mock_service

            response = authenticated_client.patch(
                "/api/chat/sessions/some-session-id",
                json={"title": "New Title"},
                content_type="application/json",
            )

            assert response.status_code == 500
            data = json.loads(response.data)
            assert data["success"] is False
            assert "Failed to update chat session" in data["error"]


class TestArchiveBlockedReturns409:
    """HTTP-layer regression test for the archive-while-running guard.

    The service layer raises ``ArchiveBlockedError`` when a chat
    session has a ResearchHistory row in ``status='in_progress'`` for
    it. The route MUST turn that into a 409 with the exact hard-coded
    message (never echo ``str(exc)`` here — CWE-209). Without
    this test, a regression that drops the ``except ArchiveBlockedError``
    branch, changes the status code, or starts interpolating exception
    text into the response would slip past the suite — the existing
    service-layer test in test_chat_archive_blocked_in_progress.py
    only covers the raise, not the route wiring.
    """

    def test_archive_in_progress_returns_409_with_hardcoded_message(
        self, authenticated_client
    ):
        with patch(
            "local_deep_research.chat.routes.ChatService"
        ) as mock_service_class:
            mock_service = MagicMock()
            # The route calls get_session() first to verify existence,
            # so it must succeed before archive_session() is reached.
            mock_service.get_session.return_value = {
                "id": "sess-1",
                "title": "t",
                "status": "active",
            }
            # And on success it reads the session back for the response.
            mock_service.archive_session.side_effect = ArchiveBlockedError(
                # Deliberately use a message containing something we
                # would NOT want to leak — confirms the route does not
                # echo str(exc) into the response body.
                "research <id=secret-123> in_progress for session sess-1"
            )
            mock_service_class.return_value = mock_service

            response = authenticated_client.patch(
                "/api/chat/sessions/sess-1",
                json={"status": "archived"},
                content_type="application/json",
            )

            assert response.status_code == 409
            data = json.loads(response.data)
            assert data["success"] is False
            # Exact hard-coded message — must not include the exc text.
            assert (
                data["error"]
                == "Cannot archive: research in_progress. Stop it first."
            )
            assert "secret-123" not in data["error"]


class TestDeleteSessionExceptionHandling:
    """Tests for exception handling in delete_session endpoint."""

    def test_delete_session_db_failure_returns_500(self, authenticated_client):
        """Test that database failure in delete_session returns 500."""
        with patch(
            "local_deep_research.chat.routes.ChatService"
        ) as mock_service_class:
            mock_service = MagicMock()
            mock_service.delete_session.side_effect = SQLAlchemyError(
                "Delete operation failed"
            )
            mock_service_class.return_value = mock_service

            response = authenticated_client.delete(
                "/api/chat/sessions/some-session-id"
            )

            assert response.status_code == 500
            data = json.loads(response.data)
            assert data["success"] is False
            assert "Failed to delete chat session" in data["error"]


class TestGetMessagesExceptionHandling:
    """Tests for exception handling in get_messages endpoint."""

    def test_get_messages_db_query_failure_returns_500(
        self, authenticated_client
    ):
        """Test that database query failure in get_messages returns 500."""
        with patch(
            "local_deep_research.chat.routes.ChatService"
        ) as mock_service_class:
            mock_service = MagicMock()
            mock_service.get_session_messages.side_effect = SQLAlchemyError(
                "Query failed"
            )
            mock_service_class.return_value = mock_service

            response = authenticated_client.get(
                "/api/chat/sessions/some-session-id/messages"
            )

            assert response.status_code == 500
            data = json.loads(response.data)
            assert data["success"] is False
            assert "Failed to get chat messages" in data["error"]


class TestSendMessageExceptionHandling:
    """Tests for exception handling in send_message endpoint."""

    def test_send_message_service_failure_returns_500(
        self, authenticated_client
    ):
        """Test that service failure in send_message returns 500."""
        with patch(
            "local_deep_research.chat.routes.ChatService"
        ) as mock_service_class:
            mock_service = MagicMock()
            mock_service.get_session.side_effect = RuntimeError(
                "Service unavailable"
            )
            mock_service_class.return_value = mock_service

            response = authenticated_client.post(
                "/api/chat/sessions/some-session-id/messages",
                json={"content": "Test message", "trigger_research": False},
                content_type="application/json",
            )

            assert response.status_code == 500
            data = json.loads(response.data)
            assert data["success"] is False
            assert "Failed to send message" in data["error"]
