from datetime import datetime
from itertools import islice
from logging import getLogger
from plone import api
from plone.memoize.view import memoize
from ploneintranet.core import _
from ploneintranet.layout.interfaces import IAppView
from ploneintranet.search.interfaces import ISearchResponse
from ploneintranet.search.interfaces import ISiteSearch
from ploneintranet.todo.utils import get_review_state_hr
from ploneintranet.workspace.interfaces import ICaseManagerAware
from urllib.parse import urlencode
from zope.component import getUtility
from zope.interface import implementer
from zope.publisher.browser import BrowserView

logger = getLogger(__name__)


@implementer(IAppView)
class CaseManagerView(BrowserView):
    app_name = "case-manager"
    _b_size = 5
    title = _("label_title_app_case_manager", default="Case manager")

    @property
    @memoize
    def is_ajax(self):
        """Check if we have an ajax call"""
        requested_with = self.request.environ.get("HTTP_X_REQUESTED_WITH")
        return requested_with == "XMLHttpRequest"

    @property
    @memoize
    def batch_size(self):
        """The size for this batch"""
        return int(self.request.get("b_size", self._b_size))

    @property
    @memoize
    def batch_start(self):
        """The size for this batch"""
        return int(self.request.get("b_start", 0))

    @property
    @memoize
    def next_batch_url(self):
        """The url to retrieve the next batch

        Return empty string if we have not enough results
        """
        if len(self.cases()) < self.batch_size:
            return ""
        form = self.request.form.copy()
        form["b_start"] = self.batch_start + self.batch_size
        return "{url}/@@{view}?{query_string}".format(
            url=self.context.absolute_url(),
            view=self.__name__,
            query_string=urlencode(form),
        )

    def extract_date_field(self, fieldname):
        """Extract the date field as a datetime object if possible,
        otherwise return None
        """
        value = self.request.form.get(fieldname)
        if not value:
            return
        try:
            return datetime.strptime(value, "%Y-%m-%d")
        except ValueError:
            # we suppose we have a date in the wrong format in this case
            return None

    def date_query(self):
        """Look for old the possible date parameters and
        return a dict to update the original query
        """
        date_query = {}
        for date_field in ["created", "modified"]:  # BBB, 'due']:
            earliest = self.extract_date_field("earliest_" + date_field)
            latest = self.extract_date_field("latest_" + date_field)
            if earliest and latest:
                date_query[date_field + "__range"] = (earliest, latest)
            elif earliest:
                date_query[date_field + "__gt"] = earliest
            elif latest:
                date_query[date_field + "__lt"] = latest
        return date_query

    def brain2view(self, brain):
        """Try to fetch the view for the given brain.
        This is not always possible because in some cases the uses has permission
        to search the case ("Access contents information")
        but has no permission to actually view it
        """
        obj = brain.getObject()
        if obj and api.user.has_permission(permission="View", obj=obj):
            return api.content.get_view("view", obj, self.request)

    @memoize
    def cases(self):
        portal = api.portal.get()
        ws_folder = portal.get("workspaces")
        ws_path = "/".join(ws_folder.getPhysicalPath())

        form = self.request.form

        query = {"object_provides": ICaseManagerAware.__identifier__, "path": ws_path}

        case_status = form.get("status")

        states = self.get_states()

        include_archived = form.get("archived", False)
        if not include_archived:
            query["is_archived"] = False

        if case_status in states:
            query["review_state"] = case_status

        pc = api.portal.get_tool("portal_catalog")
        valid_indexes = tuple(pc.indexes())
        for field in valid_indexes:
            if form.get(field):
                query[field] = form.get(field)

        solr_query = self.prepare_solr_query(
            phrase=form.get("SearchableText"), query=query
        )

        date_query = self.date_query()
        if date_query:
            solr_query = solr_query.filter(**date_query)

        response = solr_query.execute()
        results = [i for i in ISearchResponse(response)]

        sort_by = "modified"
        if sort_by == "modified":
            results.sort(key=lambda item: item.modified, reverse=True)
        else:
            results.sort(key=lambda item: item.title.lower())

        b_size = self.batch_size
        b_start = self.batch_start
        return tuple(islice(results, b_start, b_start + b_size))

    def get_field_indexes(self, field):
        catalog = api.portal.get_tool("portal_catalog")
        try:
            indexes = [
                i
                for i in catalog._catalog.getIndex(field).uniqueValues()
                if i is not None
            ]
        except KeyError:
            logger.warning("Missing index: %s", field)
            indexes = []
        return indexes

    def get_states(self):
        """Returns only the states known to be in use by cases"""
        states = api.portal.get_registry_record(
            "ploneintranet.workspace.case_manager.states"
        )
        return states

    @memoize
    def get_review_state_title(self, review_state):
        workflow = api.portal.get_tool("portal_workflow")
        titles = {
            get_review_state_hr(wf, review_state) for wf in workflow.objectValues()
        }
        if len(titles) > 1 and review_state in titles:
            titles.remove(review_state)
        return "/".join(sorted(titles))

    def staff_prefill(self):
        staff = self.request.get("staff")
        if not staff:
            return ""
        user = api.user.get(username=staff)
        if user:
            return '{{"{0}": "{1} <{2}>"}}'.format(
                staff, user.getProperty("fullname") or staff, user.getProperty("email")
            )
        return ""

    def scheduled_session_prefill(self):
        scheduled_session = self.request.get("scheduled_session")
        if not scheduled_session:
            return ""
        catalog = api.portal.get_tool("portal_catalog")
        brains = catalog(UID=scheduled_session)
        if len(brains):
            # If len > 1 then all bets are off anyway. Ignoring.
            brain = brains[0]
            return '{{"{0}": "{1} - {2}"}}'.format(
                scheduled_session, brain["start"].strftime("%d.%m.%Y"), brain["Title"]
            )
        return ""

    def prepare_solr_query(self, phrase=None, query=None):
        sitesearch = getUtility(ISiteSearch)
        solr_query = sitesearch._create_query_object(phrase)
        solr_query = sitesearch._apply_filters(solr_query, filters=query)
        solr_query = sitesearch._apply_facets(solr_query)
        solr_query = sitesearch._apply_security(solr_query)
        solr_query = sitesearch._paginate(solr_query, start=0, step=9999)
        return solr_query
