from logging import getLogger
from plone.memoize.view import memoize_contextless
from ploneintranet import api as pi_api
from ploneintranet.userprofile.browser.sync.base import BaseSyncManyView
from ploneintranet.userprofile.browser.sync.base import BaseSyncView

logger = getLogger(__name__)


class SyncProperties(BaseSyncView):
    """View that syncs the workgroup properties with the external source"""

    _known_external_properties = ("title", "description", "mail")

    def _safe_set(self, groupdata, key):
        """Before setting the attribute, check if the old value is equal to the old one
        to save some writes.
        Return a boolean if the new value is actually set.
        """
        if key not in groupdata:
            return False
        value = groupdata[key]
        if value == getattr(self.context, key):
            return False
        setattr(self.context, key, value)
        return True

    def _update_members(self):
        """Be sure that the workgroup members are the same as the ones of the
        external source.
        Return a truish if the members have been updated,
        a falsish if no operation was done
        """
        umembers = set(self.external_source.getGroupMembers(self.context.canonical))
        if umembers == self.context.members:
            return False

        self.context.members = umembers
        return True

    def _sync(self):
        if not self.context.canonical:
            # Cheap check to guess if this object was created manually
            return
        logger.info("Updating info for %r", self.context)
        groups = self.external_source.enumerateGroups(
            id=self.context.canonical, exact_match=True
        )
        if len(groups) != 1:
            return
        group = groups[0]

        changed = self._update_members()
        for key in self._known_external_properties:
            changed = changed or self._safe_set(group, key)

        if not changed:
            logger.info("Sync %r: no change needed" % self.context)
            return

        self.record_last_sync()
        self._notify_modified()
        logger.info("Sync %r: changed" % self.context)


class AllWorkGroupsSync(BaseSyncManyView):
    """Sync all groups from the "canonical" source of external groups.

    Compares local membrane groups to the list of groups found
    in the canonical pas source.
    Removes any groups no longer in the canonical source,
    and creates groups for any new groups found in the canonical source.
    """

    @property
    @memoize_contextless
    def external_principal_ids(self):
        """Return the group ids managed by the external source

        Raise an exception if the external source returns no principals
        to avoid disabling them all
        """
        ids = {group["id"] for group in self.external_source.enumerateGroups()}
        if not ids:
            raise RuntimeError(
                "The external source plugin is not returning any principal id!"
            )
        return ids

    @property
    @memoize_contextless
    def local_principal_ids(self):
        """Return the group ids managed by Plone"""
        return {x.getGroupId() for x in self.context.objectValues()}

    def _get_principal_by_id(self, principal_id):
        return pi_api.workgroup.get(principal_id)

    def _create_principal_by_id(self, principal_id):
        return pi_api.workgroup.create(
            groupid=principal_id, properties={"source": self.external_source_id}
        )

    def _sync(self):
        """XXX Disabling groups is not supported by any workflow"""
        # self._disable_exceeding_principals()
        self._create_missing_principals()
        self._sync_many()
