from copy import deepcopy
from plone import api
from plone.memoize.view import memoize_contextless
from ploneintranet.contacts.content.contact import IEmergencyServiceMarker
from ploneintranet.core import _
from ploneintranet.layout.interfaces import IAppView
from ploneintranet.search.interfaces import ISiteSearch
from ploneintranet.userprofile.interfaces import IFunctionalPrincipal
from Products.Five import BrowserView
from zope.component import getUtility
from zope.interface import implementer

import logging
import six.moves.urllib.error
import six.moves.urllib.parse
import six.moves.urllib.request

log = logging.getLogger(__name__)

_marker = object()


@implementer(IAppView)
class View(BrowserView):
    app_name = "contacts"
    title = _("label_title_app_contacts", default="Contacts")
    solr_step = 20

    @property
    @memoize_contextless
    def portal(self):
        return api.portal.get()

    @property
    @memoize_contextless
    def can_add_workgroups(self):
        groups = self.portal.get("groups")
        if not groups:
            return False
        return api.user.has_permission("Add portal content", obj=groups)

    @property
    @memoize_contextless
    def can_add_contacts(self):
        if "contacts" not in self.portal:
            return False
        return api.user.has_permission("Add portal content", obj=self.portal.contacts)

    @property
    @memoize_contextless
    def show_add_button(self):
        return self.can_add_workgroups or self.can_add_contacts

    @property
    @memoize_contextless
    def add_button_title(self):
        title = api.portal.translate(_("Create an emergency service"))
        # TODO: when "All contacts" is implemented, change to:
        # title = api.portal.translate(_(u"Create a contact"))
        if self.can_add_workgroups:
            title = api.portal.translate(
                _("Create an emergency service, group or division")
            )
        return title

    @property
    @memoize_contextless
    def page_idx(self):
        return int(self.request.form.get("page_idx", 0))

    @property
    @memoize_contextless
    def page_size(self):
        return int(self.request.form.get("page_size", self.solr_step))

    @property
    @memoize_contextless
    def is_searching(self):
        return bool(self.request.form.get("SearchableText", ""))

    @property
    @memoize_contextless
    def portal_types_when_browsing(self) -> list:
        """The portal_types that will be displayed by the app when browsing"""
        portal_types: tuple | object = api.portal.get_registry_record(
            "ploneintranet.contacts.app.portal_types_when_browsing",
            default=_marker,
        )

        if portal_types is _marker:
            return ["ploneintranet.userprofile.workgroup"]

        return list(portal_types)  # type: ignore

    @property
    @memoize_contextless
    def portal_types_when_searching(self) -> list:
        """The portal_types that will be displayed by the app when searching"""
        portal_types: tuple | object = api.portal.get_registry_record(
            "ploneintranet.contacts.app.portal_types_when_searching",
            default=_marker,
        )

        if portal_types is _marker:
            return [
                "ploneintranet.userprofile.workgroup",
                "ploneintranet.contacts.contact",
                "ploneintranet.userprofile.userprofile",
            ]

        return list(portal_types)  # type: ignore

    @property
    @memoize_contextless
    def excluded_review_states(self) -> list:
        """The review states to exclude from the listing"""
        review_states: tuple | object = api.portal.get_registry_record(
            "ploneintranet.userprofile.ui.excluded_review_states",
            default=_marker,
        )
        if review_states is _marker:
            return ["disabled", "pending"]

        return list(review_states)  # type: ignore

    @property
    @memoize_contextless
    def excluded_object_provides(self) -> list:
        """The list of interfaces to exclude from the listing"""
        object_provides: tuple | object = api.portal.get_registry_record(
            "ploneintranet.userprofile.ui.excluded_object_provides",
            default=_marker,
        )
        if object_provides is _marker:
            return [IFunctionalPrincipal.__identifier__]

        return list(object_provides)  # type: ignore

    @property
    @memoize_contextless
    def query_filters(self) -> dict:
        """Returns query filters for the contacts app.

        In the sidebar, we want to initially display only some content types,
        e.g. workgroups, but when searching,
        we want to include all the relevant content types.
        """
        filters = {
            "-object_provides": self.excluded_object_provides,
            "-review_state": self.excluded_review_states,
        }

        if self.is_searching:
            filters["portal_type"] = self.portal_types_when_searching
        else:
            filters["portal_type"] = self.portal_types_when_browsing

        return filters

    @property
    @memoize_contextless
    def query_more(self):
        if not len(self.principals()) == self.page_size:
            return ""
        params = self.request.form.copy()
        params["page_idx"] = self.page_idx + 1
        return six.moves.urllib.parse.urlencode(params, doseq=True)

    @memoize_contextless
    def contact_counts(self):
        query_filters = deepcopy(self.query_filters)
        counts = {
            "Contact": 0,
            "Emergency Service": 0,
        }

        search = getUtility(ISiteSearch)
        query_filters["portal_type"] = ["ploneintranet.contacts.contact"]
        response = search.query(
            filters=query_filters,
            start=0,
            step=0,
        )
        counts.update(
            {
                entry["name"]: entry["count"]
                for entry in response.facets["friendly_type_name"]
                if entry["name"] in counts
            }
        )
        # Users and groups are both "Contact". Get user count separately.
        query_filters["portal_type"] = "ploneintranet.userprofile.userprofile"
        response = search.query(
            filters=query_filters,
            start=0,
            step=0,
        )
        counts["User"] = response.total_results
        return counts

    @memoize_contextless
    def principals(self):
        """(first) page of matching users and groups"""
        search = getUtility(ISiteSearch)
        filters = self.query_filters
        searchterm = self.request.form.get("SearchableText", "")
        if searchterm:
            filters["SearchableText"] = searchterm

        if self.page_size == -1:
            start = 0
            step = 999999
        else:
            start = self.page_idx * self.page_size
            step = self.page_size

        if not api.portal.get_registry_record(
            "ploneintranet.userprofile.extranet_enabled", default=False
        ):
            try:
                filters["-UID"] = api.portal.get().groups.intranet.UID()
            except AttributeError:
                pass
        response = search.query(
            filters=filters,
            sort=["-portal_type", "sortable_title"],
            start=start,
            step=step,
        )
        return tuple(response)

    @property
    @memoize_contextless
    def extra_navigation_items(self):
        contacts_container = self.portal.get("contacts", None)
        if not contacts_container:
            return []

        catalog = api.portal.get_tool("portal_catalog")
        query = {
            "portal_type": "Link",
            "sort_on": "sortable_title",
            "path": {
                "query": "/".join(contacts_container.getPhysicalPath()),
                "depth": 1,
            },
        }
        brains = catalog(query)
        return brains


class EmergencyServicesView(View):
    @property
    @memoize_contextless
    def query_filters(self):
        return {
            "portal_type": [
                "ploneintranet.contacts.contact",
            ],
            "object_provides": IEmergencyServiceMarker.__identifier__,
        }


class RedirectToApp(BrowserView):
    """The immediate view for folders in Quaive is the listing_view,
    but that is switchedoff in ploneintrtranet

    We define a custom view for the folders in the contacts container
    to redirect to the contacts app in order to not have unwanted 404
    """

    def __call__(self):
        app = api.portal.get().apps[self.context.id]
        self.request.response.redirect(app.absolute_url())
