from collections import Counter
from plone import api
from plone.batching import Batch
from ploneintranet.search import base
from ploneintranet.search.interfaces import ISearchResponse
from ploneintranet.search.interfaces import ISearchResult
from ploneintranet.search.interfaces import ISiteSearch
from zope.component import adapter
from zope.interface import implementer

import Missing


@implementer(ISiteSearch)
class SiteSearch(base.SiteSearch):
    """Example implementation of ISiteSearch using the Plone catalog."""

    _apply_facets = base.NoOpQueryMethod()
    _apply_spellchecking = base.NoOpQueryMethod()
    _apply_boost_query = base.NoOpQueryMethod()
    _apply_security = base.NoOpQueryMethod()

    def _create_query_object(self, phrase):
        if phrase:
            # Poor man's partial word matching
            phrase = phrase.strip()
            if phrase and not phrase.endswith("*"):
                phrase = phrase + "*"
            return dict(SearchableText=phrase)
        else:
            return {}

    def _apply_debug(self, query):
        from pprint import pprint

        pprint(query)
        return query

    def _apply_filters(self, query, filters):
        # Do not apply empty filters to the catalog
        query = {
            key: value for key, value in dict(query).items() if value or value is False
        }
        filters = {
            key: value for key, value in filters.items() if value or value is False
        }
        # Rename tags in Subject
        if "tags" in filters:
            filters["Subject"] = filters.pop("tags")
        query.update(filters)
        return query

    def _apply_date_range(self, query, start_date=None, end_date=None):
        query = dict(query, created=dict.fromkeys(("query", "range")))
        created = query["created"]
        if all((start_date, end_date)):
            created["query"] = (start_date, end_date)
            created["range"] = "min:max"
        elif start_date is not None:
            created["query"] = start_date
            created["range"] = "min"
        else:
            created["query"] = end_date
            created["range"] = "max"
        return query

    def _paginate(self, query, start, step):
        return dict(query, batch_start=start, batch_step=step)

    def _wrap_query_object(self, query):
        return query

    def execute(self, query, secure=True, **kw):
        """The sort parameter"""
        start = query.pop("batch_start", 0)
        step = query.pop("batch_step", 100)
        catalog = api.portal.get_tool(name="portal_catalog")
        if secure:
            search = catalog.searchResults
        else:
            search = catalog.unrestrictedSearchResults
        sort = kw.get("sort")
        if sort and isinstance(sort, str):
            # valid sort values:
            #  - 'created': sort results ascending by creation date
            #  - '-created': sort results descending by creation date
            #  - 'title': sort results ascending by title
            if sort == "title":
                sort = "sortable_title"
            if sort.startswith("-"):
                query["sort_order"] = "descending"
                query["sort_on"] = sort[1:]
            else:
                query["sort_on"] = sort

        brains = search(query)
        return Batch(brains, step, start)


@implementer(ISearchResponse)
@adapter(Batch)
class SearchResponse(base.SearchResponse):
    """Adapter for a ZCatalog search response.

    Implements batching.
    """

    facet_fields = base.RegistryProperty("facet_fields")
    facets = {}

    def __init__(self, batched_results):
        all_results = batched_results._sequence
        super().__init__(SearchResult(result, self) for result in batched_results)
        self.total_results = batched_results.sequence_length

        if not self.total_results:
            return

        # Faceting is not supported natively by plone's catalog,
        # so we brute force it here. Recommendation: USE SOLR
        for field in self.facet_fields:
            if field == "tags":
                catalog_field = "Subject"
            else:
                catalog_field = field

            values = []
            my_results = [
                x[catalog_field]
                for x in all_results
                if x[catalog_field] != Missing.Value
            ]
            if len(my_results):
                for my_result in my_results:
                    if hasattr(my_result, "__iter__"):
                        values.extend(my_result)
                    else:
                        values.append(my_result)
            # from ploneintranet.suite.tests import robot_trace; robot_trace()
            if values:
                values = (
                    isinstance(x, list) and ", ".join(x) or x
                    for x in values
                    if x != Missing.Value
                )
                values = [
                    {"name": name, "count": count}
                    for name, count in Counter(values).items()
                ]
            self.facets[field] = values


@implementer(ISearchResult)
class SearchResult(base.SearchResult):
    """Build a Search result from a ZCatalog brain
    and an ISearchResponse
    """

    def getObject(self):
        return self.context.getObject()

    @property
    def path(self):
        return self.context.getPath()

    @property
    def review_state(self):
        return self.context.review_state
