import re
import logging
from itertools import chain

from ming import schema
from ming.utils import LazyProperty
from ming.orm import FieldProperty, RelationProperty, ForeignIdProperty, Mapper

from allura import model as M
from allura.lib import utils
from allura.lib import helpers as h

config = utils.ConfigProxy(
    common_suffix='forgemail.domain')

log = logging.getLogger(__name__)

class Forum(M.Discussion):
    class __mongometa__:
        name='forum'
    type_s = 'Discussion'

    parent_id = FieldProperty(schema.ObjectId, if_missing=None)
    threads = RelationProperty('ForumThread')
    posts = RelationProperty('ForumPost')
    members_only = FieldProperty(bool, if_missing=False)
    anon_posts = FieldProperty(bool, if_missing=False)
    monitoring_email = FieldProperty(str, if_missing=None)

    @classmethod
    def attachment_class(cls):
        return ForumAttachment

    @classmethod
    def thread_class(cls):
        return ForumThread

    @LazyProperty
    def threads(self):
        threads = self.thread_class().query.find(dict(discussion_id=self._id)).all()
        sorted_threads = chain(
            (t for t in threads if 'Announcement' in t.flags),
            (t for t in threads if 'Sticky' in t.flags and 'Announcement' not in t.flags),
            (t for t in threads if 'Sticky' not in t.flags and 'Announcement' not in t.flags))
        return list(sorted_threads)

    @property
    def parent(self):
        return Forum.query.get(_id=self.parent_id)

    @property
    def subforums(self):
        return Forum.query.find(dict(parent_id=self._id)).all()

    @property
    def email_address(self):
        domain = '.'.join(reversed(self.app.url[1:-1].split('/'))).replace('_', '-')
        return '%s@%s%s' % (self.shortname.replace('/', '.'), domain, config.common_suffix)

    @LazyProperty
    def announcements(self):
        return self.thread_class().query.find(dict(
                app_config_id=self.app_config_id,
                flags='Announcement')).all()

    def breadcrumbs(self):
        if self.parent:
            l = self.parent.breadcrumbs()
        else:
            l = []
        return l + [(self.name, self.url())]

    def url(self):
        return h.urlquote(self.app.url + self.shortname + '/')

    def delete(self):
        # Delete the subforums
        for sf in self.subforums:
            sf.delete()
        super(Forum, self).delete()

    def get_discussion_thread(self, data=None):
        # If the data is a reply, use the parent's thread
        subject = '[no subject]'
        parent_id = None
        if data is not None:
            in_reply_to = data.get('in_reply_to')
            if in_reply_to:
                parent_id = in_reply_to[0].split('/')[-1]
            else:
                parent_id = None
            message_id = data.get('message_id') or ''
            subject = data['headers'].get('Subject', subject)
        if parent_id is not None:
            parent = self.post_class().query.get(_id=parent_id)
            if parent: return parent.thread, parent_id
        if message_id:
            post = self.post_class().query.get(_id=message_id)
            if post: return post.thread, None
        # Otherwise it's a new thread
        return self.thread_class()(discussion_id=self._id,subject=subject), None

    @property
    def discussion_thread(self):
        return None

    @property
    def icon(self):
        return ForumFile.query.get(forum_id=self._id)

class ForumFile(M.File):
    forum_id=FieldProperty(schema.ObjectId)

class ForumThread(M.Thread):
    class __mongometa__:
        name='forum_thread'
        indexes = ['flags']
    type_s = 'Thread'

    discussion_id = ForeignIdProperty(Forum)
    first_post_id = ForeignIdProperty('ForumPost')
    flags = FieldProperty([str])

    discussion = RelationProperty(Forum)
    posts = RelationProperty('ForumPost')
    first_post = RelationProperty('ForumPost', via='first_post_id')

    @property
    def status(self):
        if self.first_post:
            return self.first_post.status
        else:
            return 'ok'

    @classmethod
    def attachment_class(cls):
        return ForumAttachment

    @property
    def email_address(self):
        return self.discussion.email_address

    def primary(self):
        return self

    def post(self, subject, text, message_id=None, parent_id=None, **kw):
        post = super(ForumThread, self).post(text, message_id=message_id, parent_id=parent_id, **kw)
        if not self.first_post_id:
            self.first_post_id = post._id
            self.num_replies = 1
        h.log_action(log, 'posted').info('')
        return post

    def set_forum(self, new_forum):
        self.post_class().query.update(
            dict(discussion_id=self.discussion_id, thread_id=self._id),
            {'$set':dict(discussion_id=new_forum._id)}, multi=True)
        self.attachment_class().query.update(
            {'discussion_id':self.discussion_id, 'thread_id':self._id},
            {'$set':dict(discussion_id=new_forum._id)})
        self.discussion_id = new_forum._id

class ForumPostHistory(M.PostHistory):
    class __mongometa__:
        name='post_history'

    artifact_id = ForeignIdProperty('ForumPost')

class ForumPost(M.Post):
    class __mongometa__:
        name='forum_post'
        history_class = ForumPostHistory
    type_s = 'Post'

    discussion_id = ForeignIdProperty(Forum)
    thread_id = ForeignIdProperty(ForumThread)

    discussion = RelationProperty(Forum)
    thread = RelationProperty(ForumThread)

    @classmethod
    def attachment_class(cls):
        return ForumAttachment

    @property
    def email_address(self):
        return self.discussion.email_address

    def primary(self):
        return self

    def promote(self):
        '''Make the post its own thread head'''
        thd = self.thread_class()(
            discussion_id=self.discussion_id,
            subject=self.subject,
            first_post_id=self._id)
        self.move(thd, None)
        return thd

    def move(self, thread, new_parent_id):
        # Add a placeholder to note the move
        placeholder = self.thread.post(
            subject='Discussion moved',
            text='',
            parent_id=self.parent_id)
        placeholder.slug = self.slug
        placeholder.full_slug = self.full_slug
        placeholder.approve()
        if new_parent_id:
            parent = self.post_class().query.get(_id=new_parent_id)
        else:
            parent = None
        # Set the thread ID on my replies and attachments
        old_slug = self.slug + '/', self.full_slug + '/'
        reply_re = re.compile(self.slug + '/.*')
        self.slug, self.full_slug = self.make_slugs(parent=parent, timestamp=self.timestamp)
        placeholder.text = 'Discussion moved to [here](%s#post-%s)' % (
            thread.url(), self.slug)
        new_slug = self.slug + '/', self.full_slug + '/'
        self.discussion_id=thread.discussion_id
        self.thread_id=thread._id
        self.parent_id=new_parent_id
        self.text = 'Discussion moved from [here](%s#post-%s)\n\n%s' % (
            placeholder.thread.url(), placeholder.slug, self.text)
        reply_tree = self.query.find(dict(slug=reply_re)).all()
        for post in reply_tree:
            post.slug = new_slug[0] + post.slug[len(old_slug[0]):]
            post.full_slug = new_slug[1] + post.slug[len(old_slug[1]):]
            post.discussion_id=self.discussion_id
            post.thread_id=self.thread_id
        for post in [ self ] + reply_tree:
            for att in post.attachments:
                att.discussion_id=self.discussion_id
                att.thread_id=self.thread_id

class ForumAttachment(M.DiscussionAttachment):
    DiscussionClass=Forum
    ThreadClass=ForumThread
    PostClass=ForumPost
    class __mongometa__:
        polymorphic_identity='ForumAttachment'
    attachment_type=FieldProperty(str, if_missing='ForumAttachment')

Mapper.compile_all()
