from unittest.mock import patch

from django.test import TestCase

from wagtail.contrib.modeladmin.helpers import DjangoORMSearchHandler, WagtailBackendSearchHandler
from wagtail.tests.modeladmintest.models import Book


class FakeSearchBackend:

    search_last_called_with = None

    def search(
        self, query, model_or_queryset, fields=None, operator=None,
        order_by_relevance=True, partial_match=True
    ):
        return {
            'query': query,
            'model_or_queryset': model_or_queryset,
            'fields': fields,
            'operator': operator,
            'order_by_relevance': order_by_relevance,
            'partial_match': partial_match
        }


class TestORMSearchHandler(TestCase):
    fixtures = ['modeladmintest_test.json']

    def get_search_handler(self, search_fields=None):
        return DjangoORMSearchHandler(search_fields)

    def get_queryset(self):
        return Book.objects.all()

    def test_search_queryset_no_search_query(self):
        # When no search fields are specified, DjangoORMSearchHandler
        # returns the queryset that was passed to it
        search_handler = self.get_search_handler(search_fields=('title',))
        queryset = self.get_queryset()
        result = search_handler.search_queryset(queryset, '')
        self.assertIs(result, queryset)

    def test_search_queryset_no_search_fields(self):
        # When the search query is blank, DjangoORMSearchHandler
        # returns the queryset that was passed to it
        search_handler = self.get_search_handler()
        queryset = self.get_queryset()
        result = search_handler.search_queryset(queryset, 'Lord')
        self.assertIs(result, queryset)

    def test_search_queryset(self):
        search_handler = self.get_search_handler(search_fields=('title',))
        queryset = self.get_queryset()
        expected_result = queryset.filter(pk=1)
        result = search_handler.search_queryset(queryset, 'Lord of the rings')
        self.assertEqual(list(expected_result), list(result))

    def test_show_search_form(self):
        search_handler = self.get_search_handler(search_fields=None)
        self.assertFalse(search_handler.show_search_form)

        search_handler = self.get_search_handler(search_fields=('content',))
        self.assertTrue(search_handler.show_search_form)


class TestSearchBackendHandler(TestCase):
    def get_search_handler(self, search_fields=None):
        return WagtailBackendSearchHandler(search_fields)

    def get_queryset(self):
        return Book.objects.all()

    @patch('wagtail.contrib.modeladmin.helpers.search.get_search_backend', return_value=FakeSearchBackend())
    def test_search_queryset_no_search_query(self, mocked_method):
        # When the search query is blank, WagtailBackendSearchHandler
        # returns the queryset that was passed to it
        search_handler = self.get_search_handler(search_fields=('title',))
        queryset = self.get_queryset()
        result = search_handler.search_queryset(queryset, '')
        self.assertIs(result, queryset)

    @patch('wagtail.contrib.modeladmin.helpers.search.get_search_backend', return_value=FakeSearchBackend())
    def test_search_queryset_no_search_fields(self, mocked_method):
        # When no search fields are specified, WagtailBackendSearchHandler
        # searches on all indexed fields
        search_handler = self.get_search_handler()
        queryset = self.get_queryset()
        search_kwargs = search_handler.search_queryset(queryset, 'test')
        self.assertTrue(mocked_method.called)
        self.assertEqual(search_kwargs, dict(
            query='test',
            model_or_queryset=queryset,
            fields=None,
            operator=None,
            order_by_relevance=True,
            partial_match=True,
        ))

    @patch('wagtail.contrib.modeladmin.helpers.search.get_search_backend', return_value=FakeSearchBackend())
    def test_search_queryset_with_search_fields(self, mocked_method):
        # When no search fields are specified, WagtailBackendSearchHandler
        # searches on all indexed fields
        search_fields = ('field1', 'field2')
        search_handler = self.get_search_handler(search_fields)
        queryset = self.get_queryset()
        search_kwargs = search_handler.search_queryset(queryset, 'test')
        self.assertTrue(mocked_method.called)
        self.assertEqual(search_kwargs, dict(
            query='test',
            model_or_queryset=queryset,
            fields=search_fields,
            operator=None,
            order_by_relevance=True,
            partial_match=True,
        ))

    @patch('wagtail.contrib.modeladmin.helpers.search.get_search_backend', return_value=FakeSearchBackend())
    def test_search_queryset_preserve_order(self, get_search_backend):
        search_handler = self.get_search_handler()
        queryset = self.get_queryset()

        search_kwargs = search_handler.search_queryset(queryset, 'Lord', preserve_order=True)
        self.assertEqual(search_kwargs, dict(
            query='Lord',
            model_or_queryset=queryset,
            fields=None,
            operator=None,
            order_by_relevance=False,
            partial_match=True,
        ))

    def test_show_search_form(self):
        search_handler = self.get_search_handler(search_fields=None)
        self.assertTrue(search_handler.show_search_form)

        search_handler = self.get_search_handler(search_fields=('content',))
        self.assertTrue(search_handler.show_search_form)
