#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
This is not a real plugin, but a hack to patch up MoinMoin's page subscriber
list to be cached.

This is based on http://moinmo.in/MoinMoinBugs/GetSubscribersSlow
"""


import re

from MoinMoin import caching, user, Page


def getSubscribersCached(self, request, **kw):
    """Get all subscribers of this page. Cached version.

    @param request: the request object
    @keyword include_self: if 1, include current user (default: 0)
    @keyword return_users: if 1, return user instances (default: 0)
    @rtype: dict
    @return: lists of subscribed email addresses in a dict by language key
    """
    include_self = kw.get('include_self', self.include_self)
    return_users = kw.get('return_users', 0)

    request.clock.start('getSubscribersCached')
    # extract categories of this page
    pageList = self.getCategories(request)

    # add current page name for list matching
    pageList.append(self.page_name)

    arena = 'user'
    key = 'page_sub'

    # get or create cache file
    page_sub = {}
    cache = caching.CacheEntry(request, arena, key, scope='wiki',
                               use_pickle=True)
    if cache.exists():
        page_sub = cache.content()
    else:
        #build a cache if it doesn't exist
        cache = caching.CacheEntry(request, arena, key, scope='wiki',
                                   use_pickle=True, do_locking=False)
        # lock to stop anybody else interfering with the data while we're
        # working
        cache.lock('w')
        userlist = user.getUserList(request)
        for uid in userlist:
            subscriber = user.User(request, uid)
            # we don't care about storing entries for users without any page
            # subscriptions
            if subscriber.subscribed_pages:
                page_sub[subscriber.id] = {
                    'name': subscriber.name,
                    'email': subscriber.email,
                    'subscribed_pages': subscriber.subscribed_pages
                }
        cache.update(page_sub)
        cache.unlock()
        # to go back to the same mode as if it had existed all along
        cache.lock('r')

    if self.cfg.SecurityPolicy:
        UserPerms = self.cfg.SecurityPolicy
    else:
        from MoinMoin.security import Default as UserPerms

    # get email addresses of the all wiki user which have a profile stored;
    # add the address only if the user has subscribed to the page and
    # the user is not the current editor
    userlist = page_sub
    subscriber_list = {}

    pages = pageList[:]
    if request.cfg.interwikiname:
        pages += ["%s:%s" % (request.cfg.interwikiname, pagename) for pagename in pageList]
    # Create text for regular expression search
    text = '\n'.join(pages)

    for uid in userlist.keys():
        if uid == request.user.id and not include_self:
            continue # no self notification

        isSubscribed = False

        # This is a bit wrong if return_users=1 (which implies that the caller
        # will process
        # user attributes and may, for example choose to send an SMS)
        # So it _should_ be "not (subscriber.email and return_users)" but that
        # breaks at the moment.
        if not userlist[uid]['email']:
            continue # skip empty email addresses

        # now check patters for actual match
        for pattern in userlist[uid]['subscribed_pages']:
            if pattern in pages:
                isSubscribed = True
            try:
                pattern = re.compile(r'^%s$' % pattern, re.M)
            except re.error:
                continue
            if pattern.search(text):
                isSubscribed = True

        # only if subscribed, then read user info from file
        if isSubscribed:
            subscriber = user.User(request, uid)

            if not UserPerms(subscriber).read(self.page_name):
                continue

            lang = subscriber.language or request.cfg.language_default
            if not lang in subscriber_list:
                subscriber_list[lang] = []
            if return_users:
                subscriber_list[lang].append(subscriber)
            else:
                subscriber_list[lang].append(subscriber.email)

    request.clock.stop('getSubscribersCached')
    return subscriber_list


def updatePageSubCache(self):
    """
    When a user changes his preferences, we update the page subscriber's cache
    """

    arena = 'user'
    key = 'page_sub'

    page_sub = {}
    cache = caching.CacheEntry(self._request, arena, key, scope='wiki',
                               use_pickle=True, do_locking=False)
    if not cache.exists():
        return  # if no cache file, just don't do anything

    cache.lock('w')
    page_sub = cache.content()

    # we don't care about storing entries for users without any page
    # subscriptions
    if self.subscribed_pages:
        page_sub[self.id] = {
            'name': self.name,
            'email': self.email,
            'subscribed_pages': self.subscribed_pages
        }
    elif page_sub.get(self.id):
        del page_sub[self.id]

    cache.update(page_sub)
    cache.unlock()


def do_patch():
    """Install our uncanny patches."""

    if getattr(user.User, 'updatePageSubCache', None) is not None:
        # The patch is applied already, do nothing
        return

    Page.Page.getSubscribers = getSubscribersCached
    oldUserSave = user.User.save
    def userSave(self, *args, **kwargs):
        ret = oldUserSave(self, *args, **kwargs)
        self.updatePageSubCache()
        return ret
    user.User.updatePageSubCache = updatePageSubCache
    user.User.save = userSave
