from .. import base
from ..interfaces import ISearchResponse
from ..interfaces import ISearchResult
from .interfaces import IConnection
from .interfaces import IConnectionConfig
from .interfaces import IQuery
from .interfaces import IResponse
from .solr_search import Search
from plone import api
from plone.memoize import instance
from scorched import SolrInterface
from zope.component import adapter
from zope.interface import implementer

import logging

logger = logging.getLogger(__name__)


@implementer(IConnection)
@adapter(IConnectionConfig)
def connection(config):
    """Adapt Solr configuration to connection/query interface."""
    logger.info("Connecting to Solr on %s", config.url)
    return SolrInterface(config.url)


@implementer(IQuery)
@adapter(IConnection)
def search_query(connection):
    return Search(connection)


@implementer(ISearchResponse)
@adapter(IResponse)
class SearchResponse(base.SearchResponse):
    """A search response object"""

    _spelling_suggestion = None
    _facets = None

    def __iter__(self):
        for doc in self.context:
            yield SearchResult(doc, self)

    def __getitem__(self, key: int) -> "SearchResult":
        """Make this object subscriptable by using key as an index.

        We take the nth document from the scorched response and wrap it
        in a SearchResult object.
        """
        return SearchResult(self.context[key], self)

    def _unpack_facets(self):
        facet_fields = self.context.facet_counts.facet_fields
        named_facets = {}
        for key in facet_fields:
            value = facet_fields[key]
            field_facets = [
                {"name": name, "count": count} for (name, count) in value if count
            ]
            named_facets[key] = field_facets
        return named_facets

    def _unpack_single_suggestion(self):
        spellcheck = self.context.spellcheck
        suggestions = spellcheck.get("suggestions", [])
        if len(suggestions) < 1:
            return None
        collated = spellcheck["collations"][-1]
        return collated

    @property
    def facets(self):
        if self._facets is None:
            self._facets = self._unpack_facets()
        return self._facets

    @property
    def spell_corrected_search(self):
        if self._spelling_suggestion is None:
            self._spelling_suggestion = self._unpack_single_suggestion()
        return self._spelling_suggestion

    @property
    def total_results(self):
        return self.context.result.numFound

    def __len__(self) -> int:
        """Return the number of actual results in this response.

        This might be less than total_results if we are only returning
        a subset of the results (for example, for pagination).
        """
        return len(self.context)


@implementer(ISearchResult)
class SearchResult(base.SearchResult):
    """Build a Search result from a scorched doc (dict)
    and an ISearchResponse
    """

    @property
    def review_state(self):
        return self.context.get("review_state", "")

    @property
    def path(self):
        return self.context["path_string"]

    @instance.memoize
    def getObject(self):
        path = self.path.split("/")
        parent = api.portal.get()
        if len(path) > 1:
            parent = parent.unrestrictedTraverse(path[:-1])
        return parent.restrictedTraverse(path[-1], None)

    @property
    def Title(self):
        """Mimic the `Title` attribute of a brain"""
        return self.title

    @instance.memoize
    def _unrestrictedGetObject(self):
        parent = api.portal.get()
        return parent.unrestrictedTraverse(self.path, None)
