from Acquisition import aq_parent
from plone.restapi.deserializer import json_body
from plone.restapi.services import Service
from Products.CMFCore.utils import getToolByName
from zExceptions import BadRequest
from zope.component import getMultiAdapter
from zope.interface import alsoProvides
from zope.security import checkPermission

import plone


class BaseCopyMove(Service):
    """Base service for copy/move operations."""

    def __init__(self, context, request):
        super().__init__(context, request)
        self.portal = getMultiAdapter(
            (self.context, self.request), name="plone_portal_state"
        ).portal()
        self.portal_url = self.portal.absolute_url()
        self.catalog = getToolByName(self.context, "portal_catalog")

    def get_object(self, key):
        """Get an object by url, path or UID."""
        if key.startswith(self.portal_url):
            # Resolve by URL
            key = key[len(self.portal_url) + 1 :]
            return self.portal.restrictedTraverse(key, None)
        elif key.startswith("/"):
            # Resolve by path
            return self.portal.restrictedTraverse(key.lstrip("/"), None)
        else:
            # Resolve by UID
            brain = self.catalog(UID=key)
            if brain:
                return brain[0].getObject()

    def reply(self):
        # return 401/403 Forbidden if the user has no permission
        if not checkPermission("cmf.AddPortalContent", self.context):
            pm = getToolByName(self.context, "portal_membership")
            if bool(pm.isAnonymousUser()):
                self.request.response.setStatus(401)
            else:
                self.request.response.setStatus(403)
            return

        data = json_body(self.request)

        source = data.get("source", None)

        if not source:
            raise BadRequest("Property 'source' is required")

        # Disable CSRF protection
        if "IDisableCSRFProtection" in dir(plone.protect.interfaces):
            alsoProvides(self.request, plone.protect.interfaces.IDisableCSRFProtection)

        if not isinstance(source, list):
            source = [source]

        parents_ids = {}
        for item in source:
            obj = self.get_object(item)
            if obj is not None:
                if self.is_moving:
                    # To be able to safely move the object, the user requires
                    # permissions on the parent
                    if not checkPermission(
                        "zope2.DeleteObjects", obj
                    ) and not checkPermission("zope2.DeleteObjects", aq_parent(obj)):
                        self.request.response.setStatus(403)
                        return
                parent = aq_parent(obj)
                if parent in parents_ids:
                    parents_ids[parent].append(obj.getId())
                else:
                    parents_ids[parent] = [obj.getId()]

        results = []
        for parent, ids in parents_ids.items():
            result = self.context.manage_pasteObjects(
                cb_copy_data=self.clipboard(parent, ids)
            )
            for res in result:
                results.append(
                    {
                        "source": "{}/{}".format(parent.absolute_url(), res["id"]),
                        "target": "{}/{}".format(
                            self.context.absolute_url(), res["new_id"]
                        ),
                    }
                )
        return results

    def clipboard(self, parent, ids):
        """Get clipboard data"""
        raise NotImplementedError


class Copy(BaseCopyMove):
    """Copies existing content objects."""

    is_moving = False

    def clipboard(self, parent, ids):
        return parent.manage_copyObjects(ids=ids)


class Move(BaseCopyMove):
    """Moves existing content objects."""

    is_moving = True

    def clipboard(self, parent, ids):
        return parent.manage_cutObjects(ids=ids)
