"""Coverage tests for web/queue/manager.py — QueueManager static methods."""

from contextlib import contextmanager
from datetime import datetime
from unittest.mock import MagicMock, patch

MODULE = "local_deep_research.web.queue.manager"


def _fake_session_ctx(session=None):
    if session is None:
        session = MagicMock()

    @contextmanager
    def ctx(username=None):
        yield session

    return ctx


def _make_queued(research_id="r1", position=1, query="q", mode="quick"):
    q = MagicMock()
    q.research_id = research_id
    q.query = query
    q.mode = mode
    q.position = position
    q.created_at = datetime(2025, 1, 1)
    q.is_processing = False
    return q


def _make_research(id="r1"):
    r = MagicMock()
    r.id = id
    return r


class TestAddToQueue:
    def test_success(self):
        from local_deep_research.web.queue.manager import QueueManager

        ms = MagicMock()
        ms.query.return_value.filter_by.return_value.scalar.return_value = 3

        with (
            patch(f"{MODULE}.get_user_db_session", _fake_session_ctx(ms)),
            patch(f"{MODULE}.queue_processor"),
            patch("local_deep_research.notifications.send_queue_notification"),
            patch("local_deep_research.settings.SettingsManager"),
        ):
            pos = QueueManager.add_to_queue("user", "r1", "query", "quick", {})

        assert pos == 4
        ms.add.assert_called_once()
        ms.commit.assert_called_once()

    def test_first_item_empty_queue(self):
        from local_deep_research.web.queue.manager import QueueManager

        ms = MagicMock()
        ms.query.return_value.filter_by.return_value.scalar.return_value = None

        with (
            patch(f"{MODULE}.get_user_db_session", _fake_session_ctx(ms)),
            patch(f"{MODULE}.queue_processor"),
            patch("local_deep_research.notifications.send_queue_notification"),
            patch("local_deep_research.settings.SettingsManager"),
        ):
            assert (
                QueueManager.add_to_queue("user", "r1", "q", "quick", {}) == 1
            )

    def test_notification_exception_swallowed(self):
        from local_deep_research.web.queue.manager import QueueManager

        ms = MagicMock()
        ms.query.return_value.filter_by.return_value.scalar.return_value = 0

        with (
            patch(f"{MODULE}.get_user_db_session", _fake_session_ctx(ms)),
            patch(f"{MODULE}.queue_processor"),
            patch(
                "local_deep_research.notifications.send_queue_notification",
                side_effect=RuntimeError("notify fail"),
            ),
            patch("local_deep_research.settings.SettingsManager"),
        ):
            assert (
                QueueManager.add_to_queue("user", "r1", "q", "quick", {}) == 1
            )

    def test_queue_processor_exception_swallowed(self):
        from local_deep_research.web.queue.manager import QueueManager

        ms = MagicMock()
        ms.query.return_value.filter_by.return_value.scalar.return_value = 0
        mock_qp = MagicMock()
        mock_qp.notify_research_queued.side_effect = RuntimeError("qp fail")

        with (
            patch(f"{MODULE}.get_user_db_session", _fake_session_ctx(ms)),
            patch(f"{MODULE}.queue_processor", mock_qp),
            patch("local_deep_research.notifications.send_queue_notification"),
            patch("local_deep_research.settings.SettingsManager"),
        ):
            assert (
                QueueManager.add_to_queue("user", "r1", "q", "quick", {}) == 1
            )


class TestGetQueuePosition:
    def test_found(self):
        from local_deep_research.web.queue.manager import QueueManager

        queued = _make_queued(position=5)
        ms = MagicMock()
        query_mock = MagicMock()
        ms.query.return_value = query_mock
        query_mock.filter_by.return_value.first.return_value = queued
        query_mock.filter.return_value.count.return_value = 2

        with patch(f"{MODULE}.get_user_db_session", _fake_session_ctx(ms)):
            assert QueueManager.get_queue_position("user", "r1") == 3

    def test_not_found(self):
        from local_deep_research.web.queue.manager import QueueManager

        ms = MagicMock()
        ms.query.return_value.filter_by.return_value.first.return_value = None

        with patch(f"{MODULE}.get_user_db_session", _fake_session_ctx(ms)):
            assert QueueManager.get_queue_position("user", "nope") is None

    def test_first_in_queue(self):
        from local_deep_research.web.queue.manager import QueueManager

        queued = _make_queued(position=1)
        ms = MagicMock()
        query_mock = MagicMock()
        ms.query.return_value = query_mock
        query_mock.filter_by.return_value.first.return_value = queued
        query_mock.filter.return_value.count.return_value = 0

        with patch(f"{MODULE}.get_user_db_session", _fake_session_ctx(ms)):
            assert QueueManager.get_queue_position("user", "r1") == 1


class TestRemoveFromQueue:
    def test_found_and_removed(self):
        from local_deep_research.web.queue.manager import QueueManager

        queued = _make_queued(position=2)
        ms = MagicMock()
        ms.query.return_value.filter_by.return_value.first.return_value = queued

        with patch(f"{MODULE}.get_user_db_session", _fake_session_ctx(ms)):
            assert QueueManager.remove_from_queue("user", "r1") is True

        ms.delete.assert_called_once_with(queued)
        ms.commit.assert_called_once()

    def test_not_found(self):
        from local_deep_research.web.queue.manager import QueueManager

        ms = MagicMock()
        ms.query.return_value.filter_by.return_value.first.return_value = None

        with patch(f"{MODULE}.get_user_db_session", _fake_session_ctx(ms)):
            assert QueueManager.remove_from_queue("user", "nope") is False


class TestGetUserQueue:
    def test_with_items(self):
        from local_deep_research.web.queue.manager import QueueManager

        queued = _make_queued(research_id="r1", position=1)
        research = _make_research(id="r1")

        ms = MagicMock()
        query_mock = MagicMock()
        ms.query.return_value = query_mock
        query_mock.filter_by.return_value.order_by.return_value.all.return_value = [
            queued
        ]
        query_mock.filter_by.return_value.first.return_value = research

        with patch(f"{MODULE}.get_user_db_session", _fake_session_ctx(ms)):
            result = QueueManager.get_user_queue("user")

        assert len(result) == 1
        assert result[0]["research_id"] == "r1"

    def test_empty_queue(self):
        from local_deep_research.web.queue.manager import QueueManager

        ms = MagicMock()
        ms.query.return_value.filter_by.return_value.order_by.return_value.all.return_value = []

        with patch(f"{MODULE}.get_user_db_session", _fake_session_ctx(ms)):
            assert QueueManager.get_user_queue("user") == []

    def test_missing_research_skipped(self):
        from local_deep_research.web.queue.manager import QueueManager

        queued = _make_queued(research_id="r1", position=1)

        ms = MagicMock()
        query_mock = MagicMock()
        ms.query.return_value = query_mock
        query_mock.filter_by.return_value.order_by.return_value.all.return_value = [
            queued
        ]
        query_mock.filter_by.return_value.first.return_value = None

        with patch(f"{MODULE}.get_user_db_session", _fake_session_ctx(ms)):
            assert QueueManager.get_user_queue("user") == []

    def test_item_without_created_at(self):
        from local_deep_research.web.queue.manager import QueueManager

        queued = _make_queued(research_id="r1", position=1)
        queued.created_at = None
        research = _make_research(id="r1")

        ms = MagicMock()
        query_mock = MagicMock()
        ms.query.return_value = query_mock
        query_mock.filter_by.return_value.order_by.return_value.all.return_value = [
            queued
        ]
        query_mock.filter_by.return_value.first.return_value = research

        with patch(f"{MODULE}.get_user_db_session", _fake_session_ctx(ms)):
            result = QueueManager.get_user_queue("user")

        assert result[0]["created_at"] is None
