# encoding: utf-8
"""
A very basic, ORM-based backend for simple search during tests.
"""

from __future__ import absolute_import, division, print_function, unicode_literals

from warnings import warn

from django.conf import settings
from django.db.models import Q
from django.utils import six

from haystack import connections
from haystack.backends import BaseEngine, BaseSearchBackend, BaseSearchQuery, log_query, SearchNode
from haystack.inputs import PythonData
from haystack.models import SearchResult
from haystack.utils import get_model_ct_tuple

if settings.DEBUG:
    import logging

    class NullHandler(logging.Handler):
        def emit(self, record):
            pass

    ch = logging.StreamHandler()
    ch.setLevel(logging.WARNING)
    ch.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))

    logger = logging.getLogger('haystack.simple_backend')
    logger.setLevel(logging.WARNING)
    logger.addHandler(NullHandler())
    logger.addHandler(ch)
else:
    logger = None


class SimpleSearchBackend(BaseSearchBackend):
    def update(self, indexer, iterable, commit=True):
        warn('update is not implemented in this backend')

    def remove(self, obj, commit=True):
        warn('remove is not implemented in this backend')

    def clear(self, models=None, commit=True):
        warn('clear is not implemented in this backend')

    @log_query
    def search(self, query_string, **kwargs):
        hits = 0
        results = []
        result_class = SearchResult
        models = connections[self.connection_alias].get_unified_index().get_indexed_models()

        if kwargs.get('result_class'):
            result_class = kwargs['result_class']

        if kwargs.get('models'):
            models = kwargs['models']

        if query_string:
            for model in models:
                if query_string == '*':
                    qs = model.objects.all()
                else:
                    for term in query_string.split():
                        queries = []

                        for field in model._meta.fields:
                            if hasattr(field, 'related'):
                                continue

                            if not field.get_internal_type() in ('TextField', 'CharField', 'SlugField'):
                                continue

                            queries.append(Q(**{'%s__icontains' % field.name: term}))

                        qs = model.objects.filter(six.moves.reduce(lambda x, y: x | y, queries))

                hits += len(qs)

                for match in qs:
                    match.__dict__.pop('score', None)
                    app_label, model_name = get_model_ct_tuple(match)
                    result = result_class(app_label, model_name, match.pk, 0, **match.__dict__)
                    # For efficiency.
                    result._model = match.__class__
                    result._object = match
                    results.append(result)

        return {
            'results': results,
            'hits': hits,
        }

    def prep_value(self, db_field, value):
        return value

    def more_like_this(self, model_instance, additional_query_string=None,
                       start_offset=0, end_offset=None,
                       limit_to_registered_models=None, result_class=None, **kwargs):
        return {
            'results': [],
            'hits': 0
        }


class SimpleSearchQuery(BaseSearchQuery):
    def build_query(self):
        if not self.query_filter:
            return '*'

        return self._build_sub_query(self.query_filter)

    def _build_sub_query(self, search_node):
        term_list = []

        for child in search_node.children:
            if isinstance(child, SearchNode):
                term_list.append(self._build_sub_query(child))
            else:
                value = child[1]

                if not hasattr(value, 'input_type_name'):
                    value = PythonData(value)

                term_list.append(value.prepare(self))

        return (' ').join(map(six.text_type, term_list))


class SimpleEngine(BaseEngine):
    backend = SimpleSearchBackend
    query = SimpleSearchQuery
