# coding=utf-8
from datetime import datetime
from plone import api
from plone.memoize.view import memoize
from ploneintranet import api as piapi
from ploneintranet.attachments.attachments import IAttachmentStoragable
from ploneintranet.attachments.utils import extract_and_add_attachments
from ploneintranet.core import ploneintranetCoreMessageFactory as _
from ploneintranet.microblog.statusupdate import StatusUpdate
from Products.Five.browser import BrowserView

import logging


logger = logging.getLogger(__name__)


class UpdateSocialBase(BrowserView):
    """Shared base class for:
    - update-social.html
    - post-well-done.html
    - comment-well-said.html
    - panel-users
    """

    input_prefix = 'form.widgets.'
    button_prefix = 'form.buttons.'

    @property
    @memoize
    def thread_id(self):
        """ Return the thread_id
        (used e.g. for commenting other status updates)
        """
        if 'thread_id' in self.request:
            return self.request.get('thread_id')
        if hasattr(self.context, 'thread_id') and self.context.thread_id:
            thread_id = self.context.thread_id  # threaded
        elif isinstance(self.context, StatusUpdate):
            thread_id = self.context.id  # first reply
        else:
            thread_id = None  # new
        return thread_id

    @property
    @memoize
    def is_posting(self):
        """ Check if the form was submitted
        """
        return ('%sstatusupdate' % self.button_prefix) in self.request.form

    @property
    @memoize
    def microblog_context(self):
        """ The context of this microblog post
        (the portal, a workspace, and so on...)
        """
        if isinstance(self.context, StatusUpdate):
            microblog_context = self.context.microblog_context
        else:
            microblog_context = piapi.microblog.get_microblog_context(
                self.context)
        if microblog_context:
            return microblog_context
        else:
            return api.portal.get()

    @property
    def microblog_context_url(self):
        return self.microblog_context.absolute_url()

    def portal_url(self):
        return api.portal.get().absolute_url()


class UpdateSocialHandler(UpdateSocialBase):
    """Shared HTTP POST handler for:
    - post-well-done.html
    - comment-well-said.html

    So excluding update-social.html.
    """

    @property
    @memoize
    def post_text(self):
        """ The text that was posted
        """
        return self.request.form.get(('%stext' % self.input_prefix), u'')

    @property
    def post_tags(self):
        """ The tags that were added
        """
        return self.request.form.get('tags', [])

    @property
    def post_mentions(self):
        """ The mentions that were added
        """
        return self.request.form.get('mentions', [])

    @property
    @memoize
    def post_attachment(self):
        """
        The attachmented that was posted (if any)
        """
        return self.request.form.get(
            ('%sattachments' % self.input_prefix),
            None
        )

    def create_post_attachment(self, post):
        """ Check:
         - if the post supports attachments
         - if we have an attachment posted

        If both are True attach the data to the post
        """
        if not IAttachmentStoragable.providedBy(post):
            return
        if not self.post_attachment:
            return
        token = self.request.get('attachment-form-token')
        extract_and_add_attachments(
            self.post_attachment,
            post,
            workspace=self.microblog_context,
            token=token
        )

    def create_post(self):
        """ Return the post object or None

        To really proceed in the status update creation we require at least
        one of these two properties to evaluate to True
         - post_text
         - post_attachment

        If not of them evaluates to True we return None
        """
        if not (self.post_text or self.post_attachment):
            return
        post = piapi.microblog.statusupdate.create(
            text=self.post_text,
            microblog_context=self.microblog_context,
            thread_id=self.thread_id,
            mention_ids=self.post_mentions,
            tags=self.post_tags,
        )
        self.create_post_attachment(post)
        return post

    def update(self):
        """ When updating we may want to process the form data and,
        if needed, create a post
        """
        if self.is_posting:
            self.post = self.create_post()

    def __init__(self, *args, **kwargs):
        super(UpdateSocialHandler, self).__init__(*args, **kwargs)
        self.post = None

    def __call__(self, *args, **kwargs):
        """ Call the multiadapter update

        We need to call the update manually (Tile instances don't do it)
        """
        self.update()
        return super(UpdateSocialHandler, self).__call__(*args, **kwargs)


class UpdateSocialView(UpdateSocialBase):
    '''
    Show the form for creating a post or reply.
    Does not actually handle the HTTP POST.
    '''

    def form_action(self):
        if self.thread_id:
            _action = '%s/comment-well-said.html'
        else:
            _action = '%s/post-well-done.html'
        return _action % self.microblog_context_url

    @property
    def form_id(self):
        if self.thread_id:
            return 'comment_box_%s' % self.thread_id
        else:
            return 'post-box'

    @property
    @memoize
    def attachment_form_token(self):
        """ Set up a token used in the attachment form
        """
        member = api.user.get_current()
        userid = member.getId()
        return "{0}-{1}".format(
            userid,
            datetime.utcnow().strftime('%Y%m%d%H%M%S%f')
        )

    def get_pat_inject(self, form_id, thread_id):
        ''' This will return the a string to fill the
        data-pat-inject attribute

        It varies according to the fact we are creating:
         - a status update (no thread_id)
         - a comment to the status update
        '''
        if not thread_id:
            return (
                'source: #activity-stream; '
                'target: #activity-stream .activities::before && #%s'
            ) % form_id
        return (
            'source: #comment-trail-%(thread_id)s; '
            'target: #new-comment-%(thread_id)s ::element::before '
            '&& '
            'source: #microblog-%(thread_id)s; '
            'target: #microblog-%(thread_id)s '
        ) % {
            'thread_id': thread_id,
        }

    ###
    # The following properties are currently set to dummy values.
    # Reason: We want the template to already mirror the Prototype;
    # this includes various conditions based on the following properties

    @property
    @memoize
    def user(self):
        return u"test_user"

    @property
    @memoize
    def direct(self):
        return False

    @property
    @memoize
    def hideuser(self):
        return False

    @property
    @memoize
    def fixeduser(self):
        return False

    @property
    @memoize
    def placeholder(self):
        if self.thread_id or 'thread_id' in self.request.form:
            placeholder = _(
                u"leave_a_comment",
                default=u"Leave a comment..."
            )
        else:
            placeholder = _(
                u"add_statusupdate_button",
                default=u"What are you doing?"
            )
        return self.microblog_context.translate(placeholder)


class FirstCommentView(UpdateSocialHandler):
    ''' This view initializes the comment stream on contexts that still
    have no comments
    '''
    form_id = 'comment-box'
    thread_id = None
    direct = False

    @property
    @memoize
    def attachment_form_token(self):
        ''' Set up a token used in the attachment form
        '''
        member = api.user.get_current()
        userid = member.getId()
        return "{0}-{1}".format(
            userid,
            datetime.utcnow().strftime('%Y%m%d%H%M%S%f')
        )

    @memoize
    def form_action(self):
        ''' Submit to itself
        '''
        return '/'.join((self.context.absolute_url(), self.__name__))

    @property
    @memoize
    def placeholder(self):
        placeholder = _(
            u"leave_a_comment",
            default=u"Leave a comment..."
        )
        return self.context.translate(placeholder)

    def create_post(self):
        ''' We need first to initialize the content stream, then we can comment
        '''
        if not (self.post_text or self.post_attachment):
            return
        parent = piapi.microblog.statusupdate.create(
            content_context=self.context,
            action_verb=u'created',
            tags=self.context.Subject() or None,
            userid=self.context.Creator(),
            time=self.context.created().asdatetime(),
        )
        post = piapi.microblog.statusupdate.create(
            text=self.post_text,
            microblog_context=self.microblog_context,
            thread_id=parent.id,
            mention_ids=self.post_mentions,
            tags=self.post_tags,
        )
        self.create_post_attachment(post)
        return post

    def get_pat_inject(self, form_id, thread_id):
        ''' This will return the a string to fill the
        data-pat-inject attribute

        It varies according to the fact we are creating:
         - a status update (no thread_id)
         - a comment to the status update
        '''
        return (
            'source: #comments-document-comments; '
            'target: #comments-document-comments; '
        )

    def __call__(self):
        ''' Call update and see if a post was created.
        If yes return the content stream that will contain
        the newly created post and a commentbox.
        '''
        self.update()
        if not self.post:
            return super(FirstCommentView, self).__call__()
        return self.request.response.redirect(
            '%s/@@content-stream.html' % self.context.absolute_url()
        )
