from unittest.mock import ANY, Mock, call, patch

from allianceauth.utils.testing import NoSocketsTestCase

from drifters.tasks import garbage_collection


class TestTasks(NoSocketsTestCase):
    @staticmethod
    def _make_wormhole(**overrides) -> Mock:
        wormhole = Mock()
        wormhole.id = overrides.pop("id", 1)
        wormhole.lifetime = overrides.pop("lifetime", "Fresh")
        wormhole.set_archived = overrides.pop("set_archived", Mock(return_value=True))
        wormhole.set_eol = overrides.pop("set_eol", Mock(return_value=True))

        for key, value in overrides.items():
            setattr(wormhole, key, value)

        return wormhole

    @staticmethod
    def _make_queryset(items: list[Mock]) -> Mock:
        queryset = Mock()
        queryset.__iter__ = Mock(return_value=iter(items))
        queryset.count.return_value = len(items)
        queryset.exists.return_value = bool(items)
        queryset.all.return_value = items
        return queryset

    @patch("drifters.tasks.Wormhole")
    def test_garbage_collection_archives_and_sets_eol(self, mock_wormhole) -> None:
        old_hole = self._make_wormhole(id=1, lifetime="Fresh")
        old_eol_hole = self._make_wormhole(id=2, lifetime="EOL")
        decaying_hole = self._make_wormhole(id=3, lifetime="Decaying")

        mock_wormhole.objects.filter.side_effect = [
            self._make_queryset([old_hole]),
            self._make_queryset([old_eol_hole]),
            self._make_queryset([decaying_hole]),
        ]

        garbage_collection()

        old_hole.set_archived.assert_called_once_with()
        old_eol_hole.set_archived.assert_called_once_with()
        decaying_hole.set_eol.assert_called_once_with()
        self.assertEqual(mock_wormhole.objects.filter.call_count, 3)
        self.assertEqual(
            mock_wormhole.objects.filter.call_args_list,
            [
                call(created_at__lte=ANY),
                call(lifetime=mock_wormhole.Lifetime.EOL, eol_changed_at__lte=ANY),
                call(
                    lifetime__in=[mock_wormhole.Lifetime.DECAY, mock_wormhole.Lifetime.FRESH],
                    created_at__lte=ANY,
                ),
            ],
        )

    @patch("drifters.tasks.Wormhole")
    def test_garbage_collection_with_no_wormholes(self, mock_wormhole) -> None:
        mock_wormhole.objects.filter.side_effect = [
            self._make_queryset([]),
            self._make_queryset([]),
            self._make_queryset([]),
        ]

        garbage_collection()

        self.assertEqual(mock_wormhole.objects.filter.call_count, 3)
