from AccessControl import Unauthorized
from Acquisition import aq_parent
from logging import getLogger
from plone import api
from plone.memoize import ram
from plone.memoize.view import memoize
from plone.memoize.view import memoize_contextless
from plone.tiles import Tile
from ploneintranet.core import _
from ploneintranet.layout.utils import shorten
from ploneintranet.workspace.interfaces import IWorkspaceFolder
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from time import time
from urllib.parse import urlencode
from zc.relation.interfaces import ICatalog
from zope.component import getUtility
from zope.interface import implementer
from zope.interface import Interface

logger = getLogger(__name__)


@implementer(Interface)
class AdaptableWSDict:
    def __init__(self, ws_dict):
        """Transforms a dict in to a traversable object"""
        self.ws_dict = ws_dict

    def UID(self):
        return self.ws_dict["uid"]

    def absolute_url(self):
        return self.ws_dict["url"]

    def __getattribute__(self, value):
        """"""
        if value != "ws_dict" and value in self.ws_dict:
            return self.ws_dict[value]
        return super().__getattribute__(value)


class WorkspacesTile(Tile):
    index = ViewPageTemplateFile("templates/workspaces.pt")
    _tile_titles = {
        "ploneintranet.workspace.workspacefolder": _("Workspaces"),
        "ploneintranet.workspace.case": _("Cases"),
    }

    @property
    @memoize
    def available_workspace_types(self):
        """The workspace types defined in the registry"""
        workspace_type_filters = api.portal.get_registry_record(
            "ploneintranet.workspace.workspace_type_filters"
        )
        return list(workspace_type_filters.keys())

    @property
    @memoize
    def workspace_type(self):
        """
        A tile should show either Workspacefolders or Cases, not both.
        """
        # Bloody hell. Default does return both.
        return self.request.form.get("workspace_type", self.available_workspace_types)

    @property
    def title(self):
        """The tile title based on the workspace type"""
        workspace_type = self.workspace_type
        if isinstance(workspace_type, str) and workspace_type in self._tile_titles:
            return self._tile_titles.get(workspace_type)
        return _("tile_workspaces_default_tile", "Workspaces")

    @property
    @memoize
    def workspace_container(self):
        """The object we are looking workspaces in"""
        portal = api.portal.get()
        workspace_path = self.request.form.get("workspace_path", "workspaces")
        return portal.get(workspace_path)

    @property
    def workspaces_url(self):
        """The URL to get to the workspaces overview

        Add the workspace_type parameter if it is a string
        """
        workspaces_url = self.workspace_container.absolute_url()
        workspace_type = self.workspace_type
        if not isinstance(workspace_type, str):
            return workspaces_url
        return "{workspaces_url}/?workspace_type={workspace_type}".format(
            workspaces_url=workspaces_url, workspace_type=workspace_type
        )

    def shorten(self, text, length=60):
        return shorten(text, length)

    @memoize
    def workspaces(self):
        """The list of my workspaces"""
        view = api.content.get_view(
            "workspaces.html", self.workspace_container, self.request
        )
        return view.workspaces()


class WorkspaceTile(Tile):
    index = ViewPageTemplateFile("templates/workspace.tile.pt")

    def shorten(self, text, length=60):
        return shorten(text, length)

    @property
    @memoize
    def tile_uid(self):
        """return the tile uid"""
        return self.context.UID()[:6]

    @property
    @memoize_contextless
    def plone_view(self):
        """The plone_view called in the context of the portal"""
        return api.content.get_view("plone", api.portal.get(), self.request)

    @property
    @memoize_contextless
    def workspace_type_mapping(self):
        """Return the mapping between workspace types and css types
        as defined in the registry record
        ploneintranet.workspace.workspace_types_css_mapping
        """
        workspace_types_css_mapping = api.portal.get_registry_record(
            "ploneintranet.workspace.workspace_types_css_mapping",
            default="ploneintranet.workspace.case|type-case",
        )
        css_mapping = dict(
            line.partition("|")[::2] for line in workspace_types_css_mapping
        )
        return css_mapping

    @property
    def workspace_type(self):
        """Return, if found the workspace type mapping"""
        return self.workspace_type_mapping.get(self.context.portal_type, "workspace")

    def get_css_classes(self):
        """Return the css classes for this tile"""
        classes = [
            "workspace-" + self.context.id.replace(".", "-"),
            getattr(self.context, "class", ""),
            self.workspace_type,
        ]

        if self.context.division and self.context.division in self._division_ids:
            classes.append("division-" + self._division_ids[self.context.division])
        if getattr(self.context, "is_division", False):
            classes.append("division-name-{0} division-{0}".format(self.context.id))
        if getattr(self.context, "is_archived", False) or getattr(
            self.context, "archival_date", False
        ):
            classes.append("state-archived")
        return " ".join(classes)

    @property
    @memoize_contextless
    def _division_ids(self):
        """cached mapping of division uids to ids
        to not need to query catalog for each workspace tile with a division
        """
        brains = api.content.find(is_division=True, object_provides=IWorkspaceFolder)
        return {brain.UID: brain.id for brain in brains}

    @memoize
    def is_superspace(self):
        return "superspace" in self.context.portal_type

    @memoize
    def is_expandable(self):
        """Check if the tile can be expanded to show subspaces"""
        # Only superspaces may be expanded
        if not self.is_superspace():
            return False
        # We do not want wo expand superspaces in the bookmark tile
        if "@@bookmarks.workspaces.tile" in self.request.getURL():
            return False
        # We do not want wo expand superspaces in the bookmark app
        orig_env = getattr(self.request, "_orig_env", {})
        if "@@app-bookmarks" in orig_env.get("PATH_INFO", ""):
            return False
        return True

    @ram.cache(lambda *args: time() // 30)
    def subspace_uids(self):
        """A subspace has a superspace assigned to it."""
        relcatalog = getUtility(ICatalog)
        rels = relcatalog.findRelations({"from_attribute": "superspace"})
        with api.env.adopt_roles(["Manager"]):
            objs = [rel.from_object for rel in rels]
        return [obj.UID() for obj in objs if obj]

    @property
    @memoize
    def workspaces_view(self):
        """Return the workspace container view"""
        if isinstance(self.context, AdaptableWSDict):
            context = aq_parent(self.context.item.getObject())
        else:
            context = aq_parent(self.context)
        if not context:
            context = api.portal.get()["workspaces"]
        return api.content.get_view("workspaces.html", context, self.request)

    @memoize
    def superspace_qs(self):
        """Format the qs for superspaces in order to properly expand the tile"""
        if self.is_superspace():
            params = {"in_superspace": self.context.UID()}
        else:
            params = {}
        form = self.request.form
        for key in ("archived", "SearchableText", "member", "userid"):
            value = form.get(key)
            if value:
                params[key] = value
        return urlencode(params)

    @memoize
    def superspace_overview_url(self):
        """Format the qs for superspaces in order to properly expand the tile"""
        url = self.workspaces_view.context.absolute_url()
        return f"{url}?{self.superspace_qs()}"

    def can_view(self):
        """Check if the user can actually view this tile

        If it is a search result, check for the absolute_url,
        otherwise check if the user has the view permission on the context
        """
        if isinstance(self.context, AdaptableWSDict):
            return "@@panel-request-access" not in self.context.absolute_url()
        return api.user.has_permission(permission="View", obj=self.context)

    @property
    @memoize_contextless
    def use_hero_image_on_tile(self):
        return api.portal.get_registry_record(
            "ploneintranet.workspace.hero_image_on_tile",
            default=False,
        )

    @property
    def hero_image_url(self):
        """
        Return URL to the hero image, if present
        """
        if not self.use_hero_image_on_tile:
            return
        if isinstance(self.context, AdaptableWSDict):
            path = self.context.ws_dict["path"]
        else:
            path = "/".join(self.context.getPhysicalPath())
        # Fetch the @@imaging view from the context of the workspaces_view, since that
        # has already been computed
        try:
            scales = self.workspaces_view.context.restrictedTraverse(f"{path}/@@images")
        except Unauthorized:
            return
        scale = scales.scale("hero_image", "1col")
        if scale:
            return scale.url


class WorkspaceTileSmall(WorkspaceTile):
    index = ViewPageTemplateFile("templates/workspace.tile.small.pt")
