from Acquisition import aq_inner
from plone import api
from plone.api.exc import InvalidParameterError
from plone.app.iterate.interfaces import IWorkingCopy
from plone.memoize.view import memoize
from Products.Five import BrowserView


class WorkflowMenu(BrowserView):
    """The helper view the renders the workflow select"""

    _edit_permission = "Modify portal content"
    _review_permission = "Review portal content"

    @property
    @memoize
    def dx_modified(self):
        modified = self.request.form.get("dx_modified")
        if isinstance(modified, list):
            modified = max(modified)
        return modified or getattr(self.context, "dx_modified", None)

    @property
    @memoize
    def can_edit(self):
        """Check if the user can edit"""
        return api.user.has_permission(self._edit_permission, obj=self.context)

    @property
    @memoize
    def can_review(self):
        return self.can_edit or api.user.has_permission(
            self._review_permission, obj=aq_inner(self.context)
        )

    @property
    @memoize
    def wf_tool(self):
        return api.portal.get_tool("portal_workflow")

    @property
    def display_as_button(self):
        states = self.get_workflow_transitions()
        if not states or len(states) != 2:
            return False
        return states[0].get("is_initial", False) or False

    @memoize
    def _get_active_workflows(self):
        return self.wf_tool.getWorkflowsFor(aq_inner(self.context))

    @memoize
    def has_workflow(self):
        return len(self._get_active_workflows()) > 0

    @memoize
    def available(self):
        """ """
        if not self.has_workflow():
            return False
        if IWorkingCopy.providedBy(self.context):
            # Do not publish working copies
            return False
        return True

    @memoize
    def get_workflow_state(self):
        return api.content.get_state(obj=aq_inner(self.context))

    @memoize
    def get_workflow_transitions(self):
        """Return possible workflow transitions and destination state names"""
        context = aq_inner(self.context)
        # Do not provide any transition if the document is locked
        try:
            locking_info = api.content.get_view(
                "plone_lock_info", self.context, self.request
            )
        except InvalidParameterError:
            locking_info = None
        if locking_info and locking_info.is_locked_for_current_user():
            return []

        current_state_id = self.get_workflow_state()

        if current_state_id is None:
            return []

        workflows = self._get_active_workflows()
        initial_states = [wf.initial_state for wf in workflows]

        # make a dict of {state_id: state_definition} for all potential states
        available_states = {
            state: getattr(wf.states, state) for wf in workflows for state in wf.states
        }

        current_state = available_states.get(current_state_id).title
        states = [
            dict(
                action="",
                action_title="",
                current_state_id=current_state_id,
                title=current_state or current_state_id,
                new_state_id="",
                selected="selected",
                is_initial=current_state_id in initial_states,
            )
        ]

        workflow_actions = (
            action
            for action in self.wf_tool.listActionInfos(object=context)
            if action["category"] == "workflow"
        )

        states_id = [item["new_state_id"] for item in states]
        potential_states = []
        for action in workflow_actions:
            new_state_id = action["transition"].new_state_id
            # Only target states are shown in the UI. If two transitions lead
            # to the same state we want to show the state only once.
            if new_state_id not in states_id:
                title = available_states.get(new_state_id).title
                potential_states.append(
                    dict(
                        action=action["id"],
                        action_title=action["title"],
                        title=title,
                        new_state_id=new_state_id,
                        selected=None,
                    )
                )
                states_id.append(new_state_id)
        # The current state always comes first
        states.extend(sorted(potential_states, key=lambda x: x["title"]))
        return states

    def form_pat_inject_options(self):
        """Return the data-path-inject options we want to use"""
        statements = [
            "target: #workflow-state::element; source: #workflow-state::element;"
        ]
        my_type = self.context.portal_type
        # A rich document has a different base statement
        if my_type == "Document":
            # 1. the save button is outside the workflow menu
            # 2. the whole document may switch between readwrite and readonly
            statements = ["source: #document-form; target: #document-form"]
        # Content documents that can be commented need to inject the in-place
        # comment trail
        if my_type in ("Document", "File", "Link"):
            statements.append(
                "source: #comments-document-comments; "
                "target: #comments-document-comments"
            )

        return " && ".join(statements)
