from django.contrib.auth.models import User
from django.forms import model_to_dict
from django.test import TestCase
from django.urls import reverse

from bookmarks.models import Bookmark
from bookmarks.tests.helpers import BookmarkFactoryMixin


class BookmarkActionViewTestCase(TestCase, BookmarkFactoryMixin):

    def setUp(self) -> None:
        user = self.get_or_create_test_user()
        self.client.force_login(user)

    def assertBookmarksAreUnmodified(self, bookmarks: [Bookmark]):
        self.assertEqual(len(bookmarks), Bookmark.objects.count())

        for bookmark in bookmarks:
            self.assertEqual(model_to_dict(bookmark), model_to_dict(Bookmark.objects.get(id=bookmark.id)))

    def test_archive_should_archive_bookmark(self):
        bookmark = self.setup_bookmark()

        self.client.post(reverse('bookmarks:index.action'), {
            'archive': [bookmark.id],
        })

        bookmark.refresh_from_db()

        self.assertTrue(bookmark.is_archived)

    def test_can_only_archive_own_bookmarks(self):
        other_user = User.objects.create_user('otheruser', 'otheruser@example.com', 'password123')
        bookmark = self.setup_bookmark(user=other_user)

        response = self.client.post(reverse('bookmarks:index.action'), {
            'archive': [bookmark.id],
        })

        bookmark.refresh_from_db()

        self.assertEqual(response.status_code, 404)
        self.assertFalse(bookmark.is_archived)

    def test_unarchive_should_unarchive_bookmark(self):
        bookmark = self.setup_bookmark(is_archived=True)

        self.client.post(reverse('bookmarks:index.action'), {
            'unarchive': [bookmark.id],
        })
        bookmark.refresh_from_db()

        self.assertFalse(bookmark.is_archived)

    def test_unarchive_can_only_archive_own_bookmarks(self):
        other_user = User.objects.create_user('otheruser', 'otheruser@example.com', 'password123')
        bookmark = self.setup_bookmark(is_archived=True, user=other_user)

        response = self.client.post(reverse('bookmarks:index.action'), {
            'unarchive': [bookmark.id],
        })
        bookmark.refresh_from_db()

        self.assertEqual(response.status_code, 404)
        self.assertTrue(bookmark.is_archived)

    def test_delete_should_delete_bookmark(self):
        bookmark = self.setup_bookmark()

        self.client.post(reverse('bookmarks:index.action'), {
            'remove': [bookmark.id],
        })

        self.assertEqual(Bookmark.objects.count(), 0)

    def test_delete_can_only_delete_own_bookmarks(self):
        other_user = User.objects.create_user('otheruser', 'otheruser@example.com', 'password123')
        bookmark = self.setup_bookmark(user=other_user)

        response = self.client.post(reverse('bookmarks:index.action'), {
            'remove': [bookmark.id],
        })
        self.assertEqual(response.status_code, 404)
        self.assertTrue(Bookmark.objects.filter(id=bookmark.id).exists())

    def test_mark_as_read(self):
        bookmark = self.setup_bookmark(unread=True)

        self.client.post(reverse('bookmarks:index.action'), {
            'mark_as_read': [bookmark.id],
        })
        bookmark.refresh_from_db()

        self.assertFalse(bookmark.unread)

    def test_unshare_should_unshare_bookmark(self):
        bookmark = self.setup_bookmark(shared=True)

        self.client.post(reverse('bookmarks:index.action'), {
            'unshare': [bookmark.id],
        })

        bookmark.refresh_from_db()

        self.assertFalse(bookmark.shared)

    def test_can_only_unshare_own_bookmarks(self):
        other_user = User.objects.create_user('otheruser', 'otheruser@example.com', 'password123')
        bookmark = self.setup_bookmark(user=other_user, shared=True)

        response = self.client.post(reverse('bookmarks:index.action'), {
            'unshare': [bookmark.id],
        })

        bookmark.refresh_from_db()

        self.assertEqual(response.status_code, 404)
        self.assertTrue(bookmark.shared)

    def test_bulk_archive(self):
        bookmark1 = self.setup_bookmark()
        bookmark2 = self.setup_bookmark()
        bookmark3 = self.setup_bookmark()

        self.client.post(reverse('bookmarks:index.action'), {
            'bulk_action': ['bulk_archive'],
            'bulk_execute': [''],
            'bookmark_id': [str(bookmark1.id), str(bookmark2.id), str(bookmark3.id)],
        })

        self.assertTrue(Bookmark.objects.get(id=bookmark1.id).is_archived)
        self.assertTrue(Bookmark.objects.get(id=bookmark2.id).is_archived)
        self.assertTrue(Bookmark.objects.get(id=bookmark3.id).is_archived)

    def test_can_only_bulk_archive_own_bookmarks(self):
        other_user = User.objects.create_user('otheruser', 'otheruser@example.com', 'password123')
        bookmark1 = self.setup_bookmark(user=other_user)
        bookmark2 = self.setup_bookmark(user=other_user)
        bookmark3 = self.setup_bookmark(user=other_user)

        self.client.post(reverse('bookmarks:index.action'), {
            'bulk_action': ['bulk_archive'],
            'bulk_execute': [''],
            'bookmark_id': [str(bookmark1.id), str(bookmark2.id), str(bookmark3.id)],
        })

        self.assertFalse(Bookmark.objects.get(id=bookmark1.id).is_archived)
        self.assertFalse(Bookmark.objects.get(id=bookmark2.id).is_archived)
        self.assertFalse(Bookmark.objects.get(id=bookmark3.id).is_archived)

    def test_bulk_unarchive(self):
        bookmark1 = self.setup_bookmark(is_archived=True)
        bookmark2 = self.setup_bookmark(is_archived=True)
        bookmark3 = self.setup_bookmark(is_archived=True)

        self.client.post(reverse('bookmarks:archived.action'), {
            'bulk_action': ['bulk_unarchive'],
            'bulk_execute': [''],
            'bookmark_id': [str(bookmark1.id), str(bookmark2.id), str(bookmark3.id)],
        })

        self.assertFalse(Bookmark.objects.get(id=bookmark1.id).is_archived)
        self.assertFalse(Bookmark.objects.get(id=bookmark2.id).is_archived)
        self.assertFalse(Bookmark.objects.get(id=bookmark3.id).is_archived)

    def test_can_only_bulk_unarchive_own_bookmarks(self):
        other_user = User.objects.create_user('otheruser', 'otheruser@example.com', 'password123')
        bookmark1 = self.setup_bookmark(is_archived=True, user=other_user)
        bookmark2 = self.setup_bookmark(is_archived=True, user=other_user)
        bookmark3 = self.setup_bookmark(is_archived=True, user=other_user)

        self.client.post(reverse('bookmarks:archived.action'), {
            'bulk_action': ['bulk_unarchive'],
            'bulk_execute': [''],
            'bookmark_id': [str(bookmark1.id), str(bookmark2.id), str(bookmark3.id)],
        })

        self.assertTrue(Bookmark.objects.get(id=bookmark1.id).is_archived)
        self.assertTrue(Bookmark.objects.get(id=bookmark2.id).is_archived)
        self.assertTrue(Bookmark.objects.get(id=bookmark3.id).is_archived)

    def test_bulk_delete(self):
        bookmark1 = self.setup_bookmark()
        bookmark2 = self.setup_bookmark()
        bookmark3 = self.setup_bookmark()

        self.client.post(reverse('bookmarks:index.action'), {
            'bulk_action': ['bulk_delete'],
            'bulk_execute': [''],
            'bookmark_id': [str(bookmark1.id), str(bookmark2.id), str(bookmark3.id)],
        })

        self.assertIsNone(Bookmark.objects.filter(id=bookmark1.id).first())
        self.assertIsNone(Bookmark.objects.filter(id=bookmark2.id).first())
        self.assertIsNone(Bookmark.objects.filter(id=bookmark3.id).first())

    def test_can_only_bulk_delete_own_bookmarks(self):
        other_user = User.objects.create_user('otheruser', 'otheruser@example.com', 'password123')
        bookmark1 = self.setup_bookmark(user=other_user)
        bookmark2 = self.setup_bookmark(user=other_user)
        bookmark3 = self.setup_bookmark(user=other_user)

        self.client.post(reverse('bookmarks:index.action'), {
            'bulk_action': ['bulk_delete'],
            'bulk_execute': [''],
            'bookmark_id': [str(bookmark1.id), str(bookmark2.id), str(bookmark3.id)],
        })

        self.assertIsNotNone(Bookmark.objects.filter(id=bookmark1.id).first())
        self.assertIsNotNone(Bookmark.objects.filter(id=bookmark2.id).first())
        self.assertIsNotNone(Bookmark.objects.filter(id=bookmark3.id).first())

    def test_bulk_tag(self):
        bookmark1 = self.setup_bookmark()
        bookmark2 = self.setup_bookmark()
        bookmark3 = self.setup_bookmark()
        tag1 = self.setup_tag()
        tag2 = self.setup_tag()

        self.client.post(reverse('bookmarks:index.action'), {
            'bulk_action': ['bulk_tag'],
            'bulk_execute': [''],
            'bulk_tag_string': [f'{tag1.name} {tag2.name}'],
            'bookmark_id': [str(bookmark1.id), str(bookmark2.id), str(bookmark3.id)],
        })

        bookmark1.refresh_from_db()
        bookmark2.refresh_from_db()
        bookmark3.refresh_from_db()

        self.assertCountEqual(bookmark1.tags.all(), [tag1, tag2])
        self.assertCountEqual(bookmark2.tags.all(), [tag1, tag2])
        self.assertCountEqual(bookmark3.tags.all(), [tag1, tag2])

    def test_can_only_bulk_tag_own_bookmarks(self):
        other_user = User.objects.create_user('otheruser', 'otheruser@example.com', 'password123')
        bookmark1 = self.setup_bookmark(user=other_user)
        bookmark2 = self.setup_bookmark(user=other_user)
        bookmark3 = self.setup_bookmark(user=other_user)
        tag1 = self.setup_tag()
        tag2 = self.setup_tag()

        self.client.post(reverse('bookmarks:index.action'), {
            'bulk_action': ['bulk_tag'],
            'bulk_execute': [''],
            'bulk_tag_string': [f'{tag1.name} {tag2.name}'],
            'bookmark_id': [str(bookmark1.id), str(bookmark2.id), str(bookmark3.id)],
        })

        bookmark1.refresh_from_db()
        bookmark2.refresh_from_db()
        bookmark3.refresh_from_db()

        self.assertCountEqual(bookmark1.tags.all(), [])
        self.assertCountEqual(bookmark2.tags.all(), [])
        self.assertCountEqual(bookmark3.tags.all(), [])

    def test_bulk_untag(self):
        tag1 = self.setup_tag()
        tag2 = self.setup_tag()
        bookmark1 = self.setup_bookmark(tags=[tag1, tag2])
        bookmark2 = self.setup_bookmark(tags=[tag1, tag2])
        bookmark3 = self.setup_bookmark(tags=[tag1, tag2])

        self.client.post(reverse('bookmarks:index.action'), {
            'bulk_action': ['bulk_untag'],
            'bulk_execute': [''],
            'bulk_tag_string': [f'{tag1.name} {tag2.name}'],
            'bookmark_id': [str(bookmark1.id), str(bookmark2.id), str(bookmark3.id)],
        })

        bookmark1.refresh_from_db()
        bookmark2.refresh_from_db()
        bookmark3.refresh_from_db()

        self.assertCountEqual(bookmark1.tags.all(), [])
        self.assertCountEqual(bookmark2.tags.all(), [])
        self.assertCountEqual(bookmark3.tags.all(), [])

    def test_can_only_bulk_untag_own_bookmarks(self):
        other_user = User.objects.create_user('otheruser', 'otheruser@example.com', 'password123')
        tag1 = self.setup_tag()
        tag2 = self.setup_tag()
        bookmark1 = self.setup_bookmark(tags=[tag1, tag2], user=other_user)
        bookmark2 = self.setup_bookmark(tags=[tag1, tag2], user=other_user)
        bookmark3 = self.setup_bookmark(tags=[tag1, tag2], user=other_user)

        self.client.post(reverse('bookmarks:index.action'), {
            'bulk_action': ['bulk_untag'],
            'bulk_execute': [''],
            'bulk_tag_string': [f'{tag1.name} {tag2.name}'],
            'bookmark_id': [str(bookmark1.id), str(bookmark2.id), str(bookmark3.id)],
        })

        bookmark1.refresh_from_db()
        bookmark2.refresh_from_db()
        bookmark3.refresh_from_db()

        self.assertCountEqual(bookmark1.tags.all(), [tag1, tag2])
        self.assertCountEqual(bookmark2.tags.all(), [tag1, tag2])
        self.assertCountEqual(bookmark3.tags.all(), [tag1, tag2])

    def test_bulk_mark_as_read(self):
        bookmark1 = self.setup_bookmark(unread=True)
        bookmark2 = self.setup_bookmark(unread=True)
        bookmark3 = self.setup_bookmark(unread=True)

        self.client.post(reverse('bookmarks:index.action'), {
            'bulk_action': ['bulk_read'],
            'bulk_execute': [''],
            'bookmark_id': [str(bookmark1.id), str(bookmark2.id), str(bookmark3.id)],
        })

        self.assertFalse(Bookmark.objects.get(id=bookmark1.id).unread)
        self.assertFalse(Bookmark.objects.get(id=bookmark2.id).unread)
        self.assertFalse(Bookmark.objects.get(id=bookmark3.id).unread)

    def test_can_only_bulk_mark_as_read_own_bookmarks(self):
        other_user = User.objects.create_user('otheruser', 'otheruser@example.com', 'password123')
        bookmark1 = self.setup_bookmark(unread=True, user=other_user)
        bookmark2 = self.setup_bookmark(unread=True, user=other_user)
        bookmark3 = self.setup_bookmark(unread=True, user=other_user)

        self.client.post(reverse('bookmarks:index.action'), {
            'bulk_action': ['bulk_read'],
            'bulk_execute': [''],
            'bookmark_id': [str(bookmark1.id), str(bookmark2.id), str(bookmark3.id)],
        })

        self.assertTrue(Bookmark.objects.get(id=bookmark1.id).unread)
        self.assertTrue(Bookmark.objects.get(id=bookmark2.id).unread)
        self.assertTrue(Bookmark.objects.get(id=bookmark3.id).unread)

    def test_bulk_mark_as_unread(self):
        bookmark1 = self.setup_bookmark(unread=False)
        bookmark2 = self.setup_bookmark(unread=False)
        bookmark3 = self.setup_bookmark(unread=False)

        self.client.post(reverse('bookmarks:index.action'), {
            'bulk_action': ['bulk_unread'],
            'bulk_execute': [''],
            'bookmark_id': [str(bookmark1.id), str(bookmark2.id), str(bookmark3.id)],
        })

        self.assertTrue(Bookmark.objects.get(id=bookmark1.id).unread)
        self.assertTrue(Bookmark.objects.get(id=bookmark2.id).unread)
        self.assertTrue(Bookmark.objects.get(id=bookmark3.id).unread)

    def test_can_only_bulk_mark_as_unread_own_bookmarks(self):
        other_user = User.objects.create_user('otheruser', 'otheruser@example.com', 'password123')
        bookmark1 = self.setup_bookmark(unread=False, user=other_user)
        bookmark2 = self.setup_bookmark(unread=False, user=other_user)
        bookmark3 = self.setup_bookmark(unread=False, user=other_user)

        self.client.post(reverse('bookmarks:index.action'), {
            'bulk_action': ['bulk_unread'],
            'bulk_execute': [''],
            'bookmark_id': [str(bookmark1.id), str(bookmark2.id), str(bookmark3.id)],
        })

        self.assertFalse(Bookmark.objects.get(id=bookmark1.id).unread)
        self.assertFalse(Bookmark.objects.get(id=bookmark2.id).unread)
        self.assertFalse(Bookmark.objects.get(id=bookmark3.id).unread)

    def test_bulk_share(self):
        bookmark1 = self.setup_bookmark(shared=False)
        bookmark2 = self.setup_bookmark(shared=False)
        bookmark3 = self.setup_bookmark(shared=False)

        self.client.post(reverse('bookmarks:index.action'), {
            'bulk_action': ['bulk_share'],
            'bulk_execute': [''],
            'bookmark_id': [str(bookmark1.id), str(bookmark2.id), str(bookmark3.id)],
        })

        self.assertTrue(Bookmark.objects.get(id=bookmark1.id).shared)
        self.assertTrue(Bookmark.objects.get(id=bookmark2.id).shared)
        self.assertTrue(Bookmark.objects.get(id=bookmark3.id).shared)

    def test_can_only_bulk_share_own_bookmarks(self):
        other_user = User.objects.create_user('otheruser', 'otheruser@example.com', 'password123')
        bookmark1 = self.setup_bookmark(shared=False, user=other_user)
        bookmark2 = self.setup_bookmark(shared=False, user=other_user)
        bookmark3 = self.setup_bookmark(shared=False, user=other_user)

        self.client.post(reverse('bookmarks:index.action'), {
            'bulk_action': ['bulk_share'],
            'bulk_execute': [''],
            'bookmark_id': [str(bookmark1.id), str(bookmark2.id), str(bookmark3.id)],
        })

        self.assertFalse(Bookmark.objects.get(id=bookmark1.id).shared)
        self.assertFalse(Bookmark.objects.get(id=bookmark2.id).shared)
        self.assertFalse(Bookmark.objects.get(id=bookmark3.id).shared)

    def test_bulk_unshare(self):
        bookmark1 = self.setup_bookmark(shared=True)
        bookmark2 = self.setup_bookmark(shared=True)
        bookmark3 = self.setup_bookmark(shared=True)

        self.client.post(reverse('bookmarks:index.action'), {
            'bulk_action': ['bulk_unshare'],
            'bulk_execute': [''],
            'bookmark_id': [str(bookmark1.id), str(bookmark2.id), str(bookmark3.id)],
        })

        self.assertFalse(Bookmark.objects.get(id=bookmark1.id).shared)
        self.assertFalse(Bookmark.objects.get(id=bookmark2.id).shared)
        self.assertFalse(Bookmark.objects.get(id=bookmark3.id).shared)

    def test_can_only_bulk_unshare_own_bookmarks(self):
        other_user = User.objects.create_user('otheruser', 'otheruser@example.com', 'password123')
        bookmark1 = self.setup_bookmark(shared=True, user=other_user)
        bookmark2 = self.setup_bookmark(shared=True, user=other_user)
        bookmark3 = self.setup_bookmark(shared=True, user=other_user)

        self.client.post(reverse('bookmarks:index.action'), {
            'bulk_action': ['bulk_unshare'],
            'bulk_execute': [''],
            'bookmark_id': [str(bookmark1.id), str(bookmark2.id), str(bookmark3.id)],
        })

        self.assertTrue(Bookmark.objects.get(id=bookmark1.id).shared)
        self.assertTrue(Bookmark.objects.get(id=bookmark2.id).shared)
        self.assertTrue(Bookmark.objects.get(id=bookmark3.id).shared)

    def test_bulk_select_across(self):
        bookmark1 = self.setup_bookmark()
        bookmark2 = self.setup_bookmark()
        bookmark3 = self.setup_bookmark()

        self.client.post(reverse('bookmarks:index.action'), {
            'bulk_action': ['bulk_archive'],
            'bulk_execute': [''],
            'bulk_select_across': ['on'],
        })

        self.assertTrue(Bookmark.objects.get(id=bookmark1.id).is_archived)
        self.assertTrue(Bookmark.objects.get(id=bookmark2.id).is_archived)
        self.assertTrue(Bookmark.objects.get(id=bookmark3.id).is_archived)

    def test_bulk_select_across_respects_query(self):
        self.setup_numbered_bookmarks(3, prefix='foo')
        self.setup_numbered_bookmarks(3, prefix='bar')

        self.assertEqual(3, Bookmark.objects.filter(title__startswith='foo').count())

        self.client.post(reverse('bookmarks:index.action') + '?q=foo', {
            'bulk_action': ['bulk_delete'],
            'bulk_execute': [''],
            'bulk_select_across': ['on'],
        })

        self.assertEqual(0, Bookmark.objects.filter(title__startswith='foo').count())
        self.assertEqual(3, Bookmark.objects.filter(title__startswith='bar').count())

    def test_bulk_select_across_ignores_page(self):
        self.setup_numbered_bookmarks(100)

        self.client.post(reverse('bookmarks:index.action') + '?page=2', {
            'bulk_action': ['bulk_delete'],
            'bulk_execute': [''],
            'bulk_select_across': ['on'],
        })

        self.assertEqual(0, Bookmark.objects.count())

    def setup_bulk_edit_scope_test_data(self):
        # create a number of bookmarks with different states / visibility
        self.setup_numbered_bookmarks(3, with_tags=True)
        self.setup_numbered_bookmarks(3, with_tags=True, archived=True)
        self.setup_numbered_bookmarks(3,
                                      shared=True,
                                      prefix="Joe's Bookmark",
                                      user=self.setup_user(enable_sharing=True))

    def test_index_action_bulk_select_across_only_affects_active_bookmarks(self):
        self.setup_bulk_edit_scope_test_data()

        self.assertIsNotNone(Bookmark.objects.filter(title='Bookmark 1').first())
        self.assertIsNotNone(Bookmark.objects.filter(title='Bookmark 2').first())
        self.assertIsNotNone(Bookmark.objects.filter(title='Bookmark 3').first())

        self.client.post(reverse('bookmarks:index.action'), {
            'bulk_action': ['bulk_delete'],
            'bulk_execute': [''],
            'bulk_select_across': ['on'],
        })

        self.assertEqual(6, Bookmark.objects.count())
        self.assertIsNone(Bookmark.objects.filter(title='Bookmark 1').first())
        self.assertIsNone(Bookmark.objects.filter(title='Bookmark 2').first())
        self.assertIsNone(Bookmark.objects.filter(title='Bookmark 3').first())

    def test_archived_action_bulk_select_across_only_affects_archived_bookmarks(self):
        self.setup_bulk_edit_scope_test_data()

        self.assertIsNotNone(Bookmark.objects.filter(title='Archived Bookmark 1').first())
        self.assertIsNotNone(Bookmark.objects.filter(title='Archived Bookmark 2').first())
        self.assertIsNotNone(Bookmark.objects.filter(title='Archived Bookmark 3').first())

        self.client.post(reverse('bookmarks:archived.action'), {
            'bulk_action': ['bulk_delete'],
            'bulk_execute': [''],
            'bulk_select_across': ['on'],
        })

        self.assertEqual(6, Bookmark.objects.count())
        self.assertIsNone(Bookmark.objects.filter(title='Archived Bookmark 1').first())
        self.assertIsNone(Bookmark.objects.filter(title='Archived Bookmark 2').first())
        self.assertIsNone(Bookmark.objects.filter(title='Archived Bookmark 3').first())

    def test_shared_action_bulk_select_across_not_supported(self):
        self.setup_bulk_edit_scope_test_data()

        response = self.client.post(reverse('bookmarks:shared.action'), {
            'bulk_action': ['bulk_delete'],
            'bulk_execute': [''],
            'bulk_select_across': ['on'],
        })
        self.assertEqual(response.status_code, 400)

    def test_handles_empty_bookmark_id(self):
        bookmark1 = self.setup_bookmark()
        bookmark2 = self.setup_bookmark()
        bookmark3 = self.setup_bookmark()

        response = self.client.post(reverse('bookmarks:index.action'), {
            'bulk_action': ['bulk_archive'],
            'bulk_execute': [''],
        })
        self.assertEqual(response.status_code, 302)

        response = self.client.post(reverse('bookmarks:index.action'), {
            'bulk_action': ['bulk_archive'],
            'bulk_execute': [''],
            'bookmark_id': [],
        })
        self.assertEqual(response.status_code, 302)

        self.assertBookmarksAreUnmodified([bookmark1, bookmark2, bookmark3])

    def test_empty_action_does_not_modify_bookmarks(self):
        bookmark1 = self.setup_bookmark()
        bookmark2 = self.setup_bookmark()
        bookmark3 = self.setup_bookmark()

        self.client.post(reverse('bookmarks:index.action'), {
            'bookmark_id': [str(bookmark1.id), str(bookmark2.id), str(bookmark3.id)],
        })

        self.assertBookmarksAreUnmodified([bookmark1, bookmark2, bookmark3])

    def test_should_redirect_to_return_url(self):
        bookmark1 = self.setup_bookmark()
        bookmark2 = self.setup_bookmark()
        bookmark3 = self.setup_bookmark()

        url = reverse('bookmarks:index.action') + '?return_url=' + reverse('bookmarks:settings.index')
        response = self.client.post(url, {
            'bulk_action': ['bulk_archive'],
            'bulk_execute': [''],
            'bookmark_id': [str(bookmark1.id), str(bookmark2.id), str(bookmark3.id)],
        })

        self.assertRedirects(response, reverse('bookmarks:settings.index'))

    def test_should_not_redirect_to_external_url(self):
        bookmark1 = self.setup_bookmark()
        bookmark2 = self.setup_bookmark()
        bookmark3 = self.setup_bookmark()

        def post_with(return_url, follow=None):
            url = reverse('bookmarks:index.action') + f'?return_url={return_url}'
            return self.client.post(url, {
                'bulk_action': ['bulk_archive'],
                'bulk_execute': [''],
                'bookmark_id': [str(bookmark1.id), str(bookmark2.id), str(bookmark3.id)],
            }, follow=follow)

        response = post_with('https://example.com')
        self.assertRedirects(response, reverse('bookmarks:index'))
        response = post_with('//example.com')
        self.assertRedirects(response, reverse('bookmarks:index'))
        response = post_with('://example.com')
        self.assertRedirects(response, reverse('bookmarks:index'))

        response = post_with('/foo//example.com', follow=True)
        self.assertEqual(response.status_code, 404)
