from collective.workspace.interfaces import IWorkspace
from plone import api
from ploneintranet.core import _
from ploneintranet.network.notifications.adapters import BaseFollowingCategory
from ploneintranet.notifications.adapters.category_provider import BaseCategory
from ploneintranet.notifications.adapters.category_provider import BaseCategoryProvider
from ploneintranet.workspace.notifications import messages
from ploneintranet.workspace.utils import parent_workspace


class BaseWorkspaceMemberCategory(BaseCategory):
    """
    Things that happen in a workspace, for users that are explicit members, i.e.
    whose access to a workspace is not via a group or via
    accessible_to_all_users
    """

    name = "workspace_member"
    default_channels = {"desktop"}
    title = _(
        "label_updates_workspaces_i_am_a_member_of",
        default="Updates in workspaces you're a member of",
    )
    description = _(
        "message_updates_workspaces_i_am_a_member_of",
        default=(
            "You will be notified as soon as a document is "
            "created or published, or a post or reply is "
            "placed within a workspace that you are a "
            "member of."
        ),
    )


class BaseWorkspaceOpenCategory(BaseCategory):
    """
    Things that happen in an open workspace, excluding explicit members, who
    will receive a workspace_member notification instead.
    """

    name = "workspace_open"
    default_channels = set()
    title = _(
        "label_updates_workspaces_i_am_no_member_of",
        default="Open workspaces",
    )
    description = _(
        "message_updates_workspaces_i_am_no_member_of",
        default=(
            "You will be notified as soon as a document is "
            "created or published, or a post or reply is "
            "placed in a workspace that you have access to, "
            "but are not a member of."
        ),
    )


class WorkspaceContentMixin:
    """Shared logic for all workspace content categories."""

    def filter_on_view_permission(self, member_ids):
        """Do not send notifications to members who cannot view the content object."""
        member_ids = set(member_ids)  # convert BTree
        for member_id in tuple(member_ids):  # use stable copy for loop
            try:
                can_see = api.user.has_permission(
                    "View",
                    user=api.user.get(member_id),
                    obj=self.context,
                )
            except api.exc.UserNotFoundError:
                can_see = False
            if not can_see:
                member_ids.discard(member_id)
        return member_ids

    def get_message_for(self, notification, with_text=True, highlight=False):
        workspace = parent_workspace(self.context)
        enhanced_security = (
            api.portal.get_registry_record(
                name="ploneintranet.notifications.enhanced_security", default=False
            )
            and not workspace.accessible_to_intranet_users
            and notification.channel
            in {
                "email",
                "digest_daily",
                "digest_weekly",
            }
        )
        if enhanced_security:
            with_text = False
            mapping = {
                "actor": self.logged_event.actor_fullname,
                "location": _(
                    "label_a_private_workspace", default="a private workspace"
                ),
            }
        else:
            mapping = {
                "actor": self.logged_event.actor_fullname,
                "object": self.maybe_process_title(self.context.Title(), highlight),
                "location": workspace.Title(),
            }

        event_type = notification.logged_event.data.get("event_type")
        # we don't handle batch document creation/publication yet
        if event_type == "created":
            return messages.variant_created_a_document(mapping, with_text)
        elif event_type == "published":
            return messages.variant_published_a_document(mapping, with_text)

        # BBB: These are deprecated, only kept for backwards compatibility
        elif event_type == "state-changed":
            return _(
                "obj_state_changed_in_watched_workspace",
                default="${actor} modified the state of an item in ${location}",
                mapping={
                    "actor": self.logged_event.actor_fullname,
                    "location": parent_workspace(self.context).title,
                },
            )
        elif event_type == "trashed":
            return _(
                "obj_trashed_in_watched_workspace",
                default="${actor} trashed an item in ${location}",
                mapping={
                    "actor": self.logged_event.actor_fullname,
                    "location": parent_workspace(self.context).title,
                },
            )
        return super().get_message_for(notification)


class WorkspaceMemberContentCategory(
    WorkspaceContentMixin, BaseWorkspaceMemberCategory
):
    @property
    def recipient_ids(self):
        workspace = parent_workspace(self.context)
        return self.filter_on_view_permission(IWorkspace(workspace).members)


class WorkspaceOpenContentCategory(WorkspaceContentMixin, BaseWorkspaceOpenCategory):
    @property
    def recipient_ids(self):
        workspace = parent_workspace(self.context)
        if not workspace.accessible_to_intranet_users:
            return set()
        member_ids = self.all_intranet_user_ids - self.expand_principal_ids(
            IWorkspace(workspace).members
        )
        return self.filter_on_view_permission(member_ids)


class FollowingWorkspaceContentCategory(WorkspaceContentMixin, BaseFollowingCategory):
    @property
    def recipient_ids(self):
        """Everybody who follows either the workspace, the author, a matching tag,
        or the document itself."""
        ploneintranet_network = api.portal.get_tool("ploneintranet_network")
        workspace = parent_workspace(self.context)
        followers = set(
            ploneintranet_network.get_watchers("workspace", workspace.UID())
        )
        for tag in self.context.Subject():
            followers.update(set(ploneintranet_network.get_followers("tag", tag)))
        followers.update(
            set(ploneintranet_network.get_followers("user", self.context.Creator()))
        )
        followers.update(
            set(ploneintranet_network.get_followers("content", self.context.UID()))
        )
        return self.filter_on_view_permission(followers)


class WorkspaceContentCategoryProvider(BaseCategoryProvider):
    category_factories = [
        FollowingWorkspaceContentCategory,
        WorkspaceMemberContentCategory,
        WorkspaceOpenContentCategory,
    ]
