#-*- python -*-
import logging
from datetime import datetime

from ming import schema as S
from ming.orm import ThreadLocalORMSession, session

from pylons import c

from allura import model as M

from forgediscussion import model as DM

log = logging.getLogger(__name__)

def validate_import(json, username_mapping, default_username=None):
    warnings = []
    schema = make_schema(username_mapping, default_username, warnings)
    json = schema.validate(json)
    return warnings, json

def perform_import(json, username_mapping, default_username=None, create_users=False):
    if create_users: default_username=create_user

    # Validate the import, create missing users
    warnings, json = validate_import(json, username_mapping, default_username)
    for w in warnings:
        log.warning('Importing to %s/%s: %s',
                    c.project.shortname,
                    c.app.config.options.mount_point,
                    w)

    for name, forum in json.forums.iteritems():
        log.info('... %s has %d threads with %d total posts',
                 name, len(forum.threads), sum(len(t) for t in forum.threads.itervalues()))

    for name, forum in json.forums.iteritems():
        log.info('... creating %s/%s: %s',
                 c.project.shortname,
                 c.app.config.options.mount_point,
                 name)
        f = DM.Forum(
            app_config_id=c.app.config._id,
            name=forum['name'],
            shortname=forum['name'],
            description=forum['description'])
        for tid, posts in forum.threads.iteritems():
            rest, head = posts[:-1], posts[-1]
            t = DM.ForumThread(
                _id=tid,
                discussion_id=f._id,
                subject=head['subject'])
            for p in posts:
                p = create_post(f._id, t._id, p)
            t.first_post_id=p._id
            ThreadLocalORMSession.flush_all()
            t.update_stats()
        ThreadLocalORMSession.flush_all()
        f.update_stats()
    return warnings

def make_schema(user_name_map, default_username, warnings):
    USER = AlluraUser(user_name_map, default_username, warnings)
    TIMESTAMP = TimeStamp()

    POST = {
        'msg_id':str,
        'is_followup_to':str,
        'is_deleted':str,
        'thread_id':str,
        'poster_name':str,
        'poster_user':USER,
        'subject':str,
        'date':TIMESTAMP,
        'body':str,
        }

    FORUM = {
        'name': str,
        'description': str,
        'threads': { str: [ POST ] },
        }

    result = S.SchemaItem.make({
            'class':str,
            'trackers':[None],
            'forums': { str: FORUM }
            })
    return result

class AlluraUser(S.FancySchemaItem):

    def __init__(self, mapping, default_username, warnings, **kw):
        self.mapping = mapping
        self.default_username = default_username
        self.warnings = warnings
        super(AlluraUser, self).__init__(**kw)

    def _validate(self, value):
        value = S.String().validate(value)
        sf_username = self.mapping.get(value, value)
        result = M.User.by_username(sf_username)
        if result is None:
            self.warnings.append('User %s not found' % value)
            if callable(self.default_username):
                sf_username = self.default_username(value)
            else:
                sf_username = self.default_username
            self.warnings.append('... setting username to %r' % sf_username)
            result = M.User.by_username(sf_username)
            self.mapping[value] = sf_username
        return result

    def _from_python(self, value, state):
        return value.username

class TimeStamp(S.FancySchemaItem):

    def _validate(self, value):
        try:
            value = int(value)
        except TypeError:
            raise S.Invalid('%s is not int()-able' % value, value, None)
        value = datetime.utcfromtimestamp(value)
        return value

def create_user(json_username):
    allura_username = c.project.shortname + '-' + json_username
    while True:
        try:
            M.User.register(
                dict(
                    username=allura_username,
                    display_name=allura_username),
                False)
            session(M.User).flush()
            break
        except:
            raise
    return allura_username

def create_post(discussion_id, thread_id, json_post):
    p = DM.ForumPost(
        _id='%s@import' % (json_post.msg_id),
        thread_id=thread_id,
        discussion_id=discussion_id,
        timestamp=json_post.date,
        author_id=json_post.poster_user._id,
        text=json_post.body,
        status='ok')
    if json_post.is_followup_to not in ('0', '-1'):
        p.parent_id = '%s@import' % (json_post['is_followup_to'])
    return p
