import re

from corehq.apps.case_search.models import (
    CASE_SEARCH_BLACKLISTED_OWNER_ID_KEY,
    CASE_SEARCH_XPATH_QUERY_KEY,
    SEARCH_QUERY_CUSTOM_VALUE,
    UNSEARCHABLE_KEYS,
    CaseSearchConfig,
    FuzzyProperties,
)
from corehq.apps.es.case_search import CaseSearchES
from corehq.apps.case_search.const import CASE_SEARCH_MAX_RESULTS


class CaseSearchCriteria(object):
    """Compiles the case search object for the view
    """

    def __init__(self, domain, case_type, criteria):
        self.domain = domain
        self.case_type = case_type
        self.criteria = criteria

        self.config = self._get_config()
        self.search_es = self._get_initial_search_es()

        self._assemble_optional_search_params()

    def _get_config(self):
        try:
            config = (CaseSearchConfig.objects
                      .prefetch_related('fuzzy_properties')
                      .prefetch_related('ignore_patterns')
                      .get(domain=self.domain))
        except CaseSearchConfig.DoesNotExist as e:
            from corehq.util.soft_assert import soft_assert
            _soft_assert = soft_assert(
                to="{}@{}.com".format('frener', 'dimagi'),
                notify_admins=False, send_to_ops=False
            )
            _soft_assert(
                False,
                "Someone in domain: {} tried accessing case search without a config".format(self.domain),
                e
            )
            config = CaseSearchConfig(domain=self.domain)
        return config

    def _get_initial_search_es(self):
        search_es = (CaseSearchES()
                     .domain(self.domain)
                     .case_type(self.case_type)
                     .size(CASE_SEARCH_MAX_RESULTS))
        return search_es

    def _assemble_optional_search_params(self):
        self._add_include_closed()
        self._add_xpath_query()
        self._add_owner_id()
        self._add_blacklisted_owner_ids()
        self._add_daterange_queries()
        self._add_case_property_queries()

    def _add_include_closed(self):
        try:
            include_closed = self.criteria.pop('include_closed')
        except KeyError:
            include_closed = False
        if include_closed != 'True':
            self.search_es = self.search_es.is_closed(False)

    def _add_xpath_query(self):
        query = self.criteria.pop(CASE_SEARCH_XPATH_QUERY_KEY, None)
        if query:
            self.search_es = self.search_es.xpath_query(self.domain, query)

    def _add_owner_id(self):
        owner_id = self.criteria.pop('owner_id', False)
        if owner_id:
            self.search_es = self.search_es.owner(owner_id)

    def _add_blacklisted_owner_ids(self):
        blacklisted_owner_ids = self.criteria.pop(CASE_SEARCH_BLACKLISTED_OWNER_ID_KEY, None)
        if blacklisted_owner_ids is not None:
            for blacklisted_owner_id in blacklisted_owner_ids.split(' '):
                self.search_es = self.search_es.blacklist_owner_id(blacklisted_owner_id)

    def _add_case_property_queries(self):
        try:
            fuzzies = self.config.fuzzy_properties.get(
                domain=self.domain, case_type=self.case_type).properties
        except FuzzyProperties.DoesNotExist:
            fuzzies = []

        for key, value in self.criteria.items():
            if key in UNSEARCHABLE_KEYS or key.startswith(SEARCH_QUERY_CUSTOM_VALUE):
                continue
            remove_char_regexs = self.config.ignore_patterns.filter(
                domain=self.domain,
                case_type=self.case_type,
                case_property=key,
            )
            for removal_regex in remove_char_regexs:
                to_remove = re.escape(removal_regex.regex)
                value = re.sub(to_remove, '', value)
            self.search_es = self.search_es.case_property_query(key, value, fuzzy=(key in fuzzies))

    def _add_daterange_queries(self):
        # Add query for specially formatted daterange param
        #   The format is __range__YYYY-MM-DD__YYYY-MM-DD, which is
        #   used by App manager case-search feature
        pattern = re.compile(r'__range__\d{4}-\d{2}-\d{2}__\d{4}-\d{2}-\d{2}')
        drop_keys = []
        for key, val in self.criteria.items():
            if val.startswith('__range__'):
                match = pattern.match(val)
                if match:
                    [_, _, startdate, enddate] = val.split('__')
                    drop_keys.append(key)
                    self.search_es = self.search_es.date_range_case_property_query(
                        key, gte=startdate, lte=enddate)
        for key in drop_keys:
            self.criteria.pop(key)

@@ -3019,32 +3019,826 @@
 sted_owner_id)%0A%0A
+    def _add_daterange_queries(self):%0A        # Add query for specially formatted daterange param%0A        #   The format is __range__YYYY-MM-DD__YYYY-MM-DD, which is%0A        #   used by App manager case-search feature%0A        pattern = re.compile(r'__range__%5Cd%7B4%7D-%5Cd%7B2%7D-%5Cd%7B2%7D__%5Cd%7B4%7D-%5Cd%7B2%7D-%5Cd%7B2%7D')%0A        drop_keys = %5B%5D%0A        for key, val in self.criteria.items():%0A            if val.startswith('__range__'):%0A                match = pattern.match(val)%0A                if match:%0A                    %5B_, _, startdate, enddate%5D = val.split('__')%0A                    drop_keys.append(key)%0A                    self.search_es = self.search_es.date_range_case_property_query(%0A                        key, gte=startdate, lte=enddate)%0A        for key in drop_keys:%0A            self.criteria.pop(key)%0A%0A
     def _add_cas
@@ -3855,32 +3855,32 @@
 _queries(self):%0A
-
         try:%0A   
@@ -4140,16 +4140,17 @@
      if 
+(
 key in U
@@ -4210,16 +4210,68 @@
 M_VALUE)
+%0A                    or key.startswith('__range__'))
 :%0A      
@@ -4650,16 +4650,16 @@
  value)%0A
+
         
@@ -4754,798 +4754,4 @@
 s))%0A
-%0A    def _add_daterange_queries(self):%0A        # Add query for specially formatted daterange param%0A        #   The format is __range__YYYY-MM-DD__YYYY-MM-DD, which is%0A        #   used by App manager case-search feature%0A        pattern = re.compile(r'__range__%5Cd%7B4%7D-%5Cd%7B2%7D-%5Cd%7B2%7D__%5Cd%7B4%7D-%5Cd%7B2%7D-%5Cd%7B2%7D')%0A        drop_keys = %5B%5D%0A        for key, val in self.criteria.items():%0A            if val.startswith('__range__'):%0A                match = pattern.match(val)%0A                if match:%0A                    %5B_, _, startdate, enddate%5D = val.split('__')%0A                    drop_keys.append(key)%0A                    self.search_es = self.search_es.date_range_case_property_query(%0A                        key, gte=startdate, lte=enddate)%0A        for key in drop_keys:%0A            self.criteria.pop(key)%0A

address feedback