# -*- coding: utf-8 -*-

################################################################
# zopyx.plone.smashdocs
# (C) 2016,  Andreas Jung, www.zopyx.com, Tuebingen, Germany
################################################################

import os
import zipfile
import tempfile
from datetime import datetime
from hurry.filesize import size

import plone.api
from zope.interface import alsoProvides
from zope.component import getUtility
from plone.protect.interfaces import IDisableCSRFProtection
from plone.registry.interfaces import IRegistry
from plone import namedfile
from plone.app.textfield.value import RichTextValue
from Products.Five.browser import BrowserView
from Products.CMFPlone.utils import safe_unicode
from plone.protect.interfaces import IDisableCSRFProtection

from ..interfaces import ISmashdocsSettings
from ..i18n import MessageFactory as _

from zopyx.smashdocs import api


class SmashDocument(BrowserView):

    @property
    def settings(self):
        registry = getUtility(IRegistry)
        return registry.forInterface(ISmashdocsSettings)

    @property
    def smashdocs(self):
        settings = self.settings
        if not settings.client_id or not settings.client_key or not settings.partner_url:
            raise ValueError(
                'You must configure partner_url and client key + id inside the Smashdocs controlpanel')

        return api.Smashdocs(
            partner_url=settings.partner_url,
            client_id=settings.client_id,
            client_key=settings.client_key,
            group_id=self.group_id)

    @property
    def settings_as_dict(self):
        settings = self.settings
        return dict([(name, getattr(settings, name)) for name in settings.__schema__.names()])

    @property
    def is_configured(self):
        """ If Smashdocs configuration inside Plone """

        settings = self.settings
        errors = 0
        if not settings.client_id:
            errors += 1
        if not settings.client_key:
            errors += 1
        if not settings.partner_url:
            errors += 1
        return not errors

    def redirect_document(self, document_id):

        catalog = plone.api.portal.get_tool('portal_catalog')
        result = catalog(portal_type='SmashDocument', document_id=document_id)
        if result:
            return self.request.response.redirect(result[0].getURL())
        else:
            self.context.plone_utils.addPortalMessage(
                'No document found ({})'.format(document_id), 'error')
            return self.request.response.redirect(self.context.absolute_url())

    def delete_document(self, document_id):

        sd = self.smashdocs

        result = sd.delete_document(document_id)
        self.request.response.redirect(self.context.absolute_url())

    def new_document(self):

        sd = self.smashdocs
        try:
            result = sd.new_document(
                user_data=self.user_data,
                title=safe_unicode(self.context.Title()),
                description=safe_unicode(self.context.Description()),
                role=self.smashdoc_role)
        except api.SmashdocsError as e:
            self.context.plone_utils.addPortalMessage(e.msg, 'error')
            return self.request.response.redirect(self.context.absolute_url())
        self.context.document_id = result['documentId']
        self.context.reindexObject()
        self.context.plone_utils.addPortalMessage(_(u'Document created'))
        return self.request.response.redirect(self.context.absolute_url())

    def open_document(self):

        sd = self.smashdocs

        try:
            result = sd.open_document(
                document_id=self.context.document_id,
                user_data=self.user_data,
                role=self.smashdoc_role)
        except api.SmashdocsError as e:
            self.context.plone_utils.addPortalMessage(
                'Error (HTTP {}, {})'.format(e.result.status_code, e.result.content))
            return self.request.response.redirect(self.context.absolute_url())

        return self.request.response.redirect(result['documentAccessLink'])

    @property
    def user_data(self):
        user = plone.api.user.get_current()
        username = user.getUserName()
        email = user.getProperty('email') or 'dummy@zopyx.com'
        fullname = user.getProperty('fullname')
        if not fullname:
            firstname = ''
            lastname = username
        else:
            if fullname.count(' ') > 0:
                firstname, lastname = fullname.split(' ', 1)
            else:
                firstname = ''
                lastname = fullname
        company = u'Schlumpfhausen'
        data = {
            "email": safe_unicode(email),
            "firstname": safe_unicode(firstname),
            "lastname": safe_unicode(lastname),
            "userId": safe_unicode(username),
            "company": safe_unicode(company)
        }
        return data

    @property
    def plone_roles(self):
        return ', '.join(plone.api.user.get_roles(obj=self.context))

    @property
    def smashdoc_role(self):
        """ Roles of the current user in Smashdocs """
        roles = plone.api.user.get_roles(obj=self.context)
        userrole = None
        if 'SD Reader' in roles:
            userrole = 'reader'
        if 'SD Commentator' in roles:
            userrole = 'commentator'
        if 'SD Editor' in roles:
            userrole = 'editor'
        if 'SD Approver' in roles or 'Manager' in roles:
            userrole = 'approver'
        return userrole

    @property
    def smashdoc_document_url(self):
        return '{}/{}'.format(self.context.absolute_url(), '@@open-document')

    @property
    def smashdoc_new_document_url(self):
        return '{}/{}'.format(self.context.absolute_url(), '@@new-document')

    @property
    def smashdoc_upload_document_url(self):
        return '{}/{}'.format(self.context.absolute_url(), '@@upload-document')

    @property
    def smashdoc_duplicate_document_url(self):
        return '{}/{}'.format(self.context.absolute_url(), '@@duplicate-document')

    @property
    def smashdoc_archive_document_url(self):
        return '{}/{}'.format(self.context.absolute_url(), '@@archive-document')

    @property
    def smashdoc_unarchive_document_url(self):
        return '{}/{}'.format(self.context.absolute_url(), '@@unarchive-document')

    @property
    def smashdoc_actions(self):
        """ Actions available based on the current smashdoc role """

        di = None
        doc_mode = 'draft'
        is_creator = True
        can_access = False
        if self.context.document_id:
            di = self.document_info()
            doc_mode = di['status']
            creator_id = di['creatorId']
            is_creator = (self.user_id == creator_id)
            can_access = (is_creator or doc_mode == 'review')

        sd_role = self.smashdoc_role

        messages = list()

        if not self.is_configured:
            messages.append(
                _(u'Smashdocs not configured properly (check with the Plone controlpanel)'))
        elif not self.context.document_id:
            messages.append(_(u'There is no document available for editing'))
        else:
            if not is_creator and not can_access:
                messages.append(
                    _(u'You can not access the current document (only available for the creator)'))

        all_actions = dict(
            read=dict(title=_(u'Read document'),
                      id='read',
                      url=self.smashdoc_document_url),
            edit=dict(title=_(u'Edit document'),
                      id='edit',
                      url=self.smashdoc_document_url),
            comment=dict(title=_(u'Comment document'),
                         id='comment',
                         url=self.smashdoc_document_url),
            upload=dict(title=_(u'Upload document'),
                        id='upload',
                        url=self.smashdoc_upload_document_url),
            create=dict(title=_(u'Create document'),
                        id='create',
                        url=self.smashdoc_new_document_url),
            duplicate=dict(title=_(u'Duplicate document'),
                           id='duplicate',
                           url=self.smashdoc_duplicate_document_url),
            archive=dict(title=_(u'Archive document'),
                         id='archive',
                         url=self.smashdoc_archive_document_url),
            unarchive=dict(title=_(u'Unarchive document'),
                           id='unarchive',
                           url=self.smashdoc_unarchive_document_url)
        )

        actions = list()
        if sd_role == 'reader':
            if self.context.document_id and can_access:
                actions.append('comment')
        elif sd_role == 'commentator':
            if self.context.document_id and can_access:
                actions.append('comment')
        elif sd_role == 'editor':
            if self.context.document_id and can_access:
                if not di['archived']:
                    actions.extend(('edit', 'duplicate', 'comment'))
                actions.append('archive' if not di[
                               'archived'] else 'unarchive')
            else:
                actions.extend(('create', 'upload'))
        elif sd_role == 'approver':
            if self.context.document_id and can_access:
                if not di['archived']:
                    actions.extend(('edit', 'duplicate'))
                actions.append('archive' if not di[
                               'archived'] else 'unarchive')
            else:
                actions.extend(('create', 'upload'))

        result_actions = [all_actions[a] for a in actions]
        return dict(actions=result_actions, messages=messages)

    @property
    def group_id(self):
        group_id = self.context.group_id
        if group_id:
            return group_id
        parent = self.context.aq_parent
        if parent.portal_type == 'SmashFolder':
            return parent.group_id
        # fallback to global group_id
        return self.settings.group_id

    @property
    def user_id(self):
        return plone.api.user.get_current().getUserName()

    def duplicate_document(self):

        alsoProvides(self.request, IDisableCSRFProtection)

        sd = self.smashdocs
        obj = plone.api.content.copy(
            self.context, self.context.aq_parent, safe_id=True)

        try:
            result = sd.duplicate_document(
                document_id=self.context.document_id,
                creator_id=self.user_id,
                title=safe_unicode('Copy of {}'.format(self.context.Title())),
                description=safe_unicode('Copy of {}'.format(self.context.Description())))
        except api.SmashdocsError as e:
            self.context.plone_utils.addPortalMessage(e.msg, 'error')
            import transaction
            transaction.doom()
            return self.request.response.redirect(self.context.absolute_url())
        obj.document_id = result['documentId']
        obj.reindexObject()
        self.context.plone_utils.addPortalMessage(_(u'Document duplicated'))
        self.request.response.redirect(obj.absolute_url())

    def archive_document(self):

        sd = self.smashdocs

        try:
            sd.archive_document(self.context.document_id)
        except api.SmashdocsError as e:
            self.context.plone_utils.addPortalMessage(e.msg, 'error')
            return self.request.response.redirect(self.context.absolute_url())

        self.convert_sdxml()
        self.context.plone_utils.addPortalMessage(_(u'Document archived'))
        return self.request.response.redirect(self.context.absolute_url())

    def unarchive_document(self):

        sd = self.smashdocs
        try:
            sd.unarchive_document(self.context.document_id)
        except api.SmashdocsError as e:
            self.context.plone_utils.addPortalMessage(e.msg, 'error')
            return self.request.response.redirect(self.context.absolute_url())
        self.context.plone_utils.addPortalMessage(_(u'Document unarchived'))
        return self.request.response.redirect(self.context.absolute_url())

    def upload_document(self):

        alsoProvides(self.request, IDisableCSRFProtection)
        sd = self.smashdocs
        if not self.request.form['file']:
            raise RuntimeError('no data')

        suffix = '.docx' if self.request.form['file'].filename.lower().endswith('.docx') else '.zip'
        filename = tempfile.mktemp(suffix=suffix)
        with open(filename, 'wb') as fp:
            fp.write(self.request.form['file'].read())

        try:
            result = sd.upload_document(
                filename=filename,
                user_data=self.user_data,
                title=safe_unicode(self.context.Title()),
                description=safe_unicode(self.context.Description()),
                role=self.smashdoc_role)
        except api.SmashdocsError as e:
            self.context.plone_utils.addPortalMessage(e.msg, 'error')
            return self.request.response.redirect(self.context.absolute_url())
        self.context.document_id = result['documentId']
        self.context.reindexObject()
        self.context.plone_utils.addPortalMessage(_(u'Document uploaded'))
        return self.request.response.redirect(self.context.absolute_url())

    def document_info(self):

        sd = self.smashdocs

        try:
            return sd.document_info(
                document_id=self.context.document_id)
        except api.SmashdocsError as e:
            return None

    def get_documents(self, mode='draft'):

        sd = self.smashdocs

        if mode == 'draft':
            params = dict(user_id=self.user_id)
        else:
            params = dict(group_id=self.context.group_id)

        try:
            result = sd.get_documents(**params)
        except api.SmashdocsError as e:
            self.context.plone_utils.addPortalMessage(e.msg, 'error')
            return self.request.response.redirect(self.context.absolute_url())
        return result

    def progress_states(self):

        sd = self.smashdocs

        try:
            di = sd.document_info(
                document_id=self.context.document_id)
        except (TypeError, api.SmashdocsError) as e:
            di = None

        states = [
            dict(title=u'New', css_class=""),
            dict(title=u'Draft', css_class=""),
            dict(title=u'Review', css_class=""),
            dict(title=u'Archived', css_class=""),
        ]

        if not di:
            states[0]['css_class'] = 'green'

        if di:
            if di['status'] == 'draft':
                states[0]['css_class'] = 'green'
                states[1]['css_class'] = 'green'

            if di['status'] == 'review':
                states[0]['css_class'] = 'green'
                states[1]['css_class'] = 'green'
                states[2]['css_class'] = 'green'

            if di['archived']:
                states[0]['css_class'] = 'green'
                states[1]['css_class'] = 'green'
                states[2]['css_class'] = 'green'
                states[3]['css_class'] = 'green'

        return states

    def export_formats(self):

        data = [
            #            dict(name='index.pdf', download=True, format='pdf', generator_url='/@@convert-pdf'),
            dict(name='index_sdxml.zip', download=False, title=u'XML (Smashdocs)',
                 format='sdxml', generator_url='/@@convert-sdxml'),
            dict(name='index_html.zip', download=False, title=u'HTML (ZIP)',
                 format='html-zip', generator_url='/@@convert-html'),
            dict(name='html', download=False, format='html',
                 generator_url='/@@convert-html'),
            #            dict(name='index.epub', download=True, format='epub', generator_url='/@@convert-epub'),
            dict(name='index.docx', download=True,
                 format='docx', generator_url='/@@convert-docx'),
        ]

        result = list()
        for d in data:
            if d['name'] in self.context.objectIds():
                obj = self.context[d['name']]
                d['modified'] = datetime.fromtimestamp(obj.modified().timeTime())
                d['url'] = '{}/{}/'.format(self.context.absolute_url(), d['name'])
                if obj.portal_type == 'File':
                    d['size'] = obj.file.size
                    d['size_str'] = size(obj.file.size)
            result.append(d)
        return result

    def convert_docx(self):

        alsoProvides(self.request, IDisableCSRFProtection)

        sd = self.smashdocs

        templates = sd.list_templates()

        out_fn = sd.export_document(
            document_id=self.context.document_id,
            format='docx',
            user_id=self.user_id,
            template_id=templates[0]['id'])

        if 'index.docx' in self.context.objectIds():
            plone.api.content.delete(self.context['index.docx'])

        obj = plone.api.content.create(
            type='File',
            id='index.docx',
            title='index.docx',
            container=self.context)

        with open(out_fn, 'rb') as fp:
            obj.file = namedfile.NamedBlobImage(
                fp.read(),
                filename=u'index.docx',
                contentType='application/vnd.openxmlformats-officedocument.wordprocessingml.document')
        os.unlink(out_fn)
        return self.request.response.redirect(self.context.absolute_url())

    def convert_sdxml(self):

        alsoProvides(self.request, IDisableCSRFProtection)

        sd = self.smashdocs
        out_fn = sd.export_document(
            document_id=self.context.document_id,
            format='sdxml',
            user_id=self.user_id)

        if 'index_sdxml.zip' in self.context.objectIds():
            plone.api.content.delete(self.context['index_sdxml.zip'])

        obj = plone.api.content.create(
            type='File',
            id='index_sdxml.zip',
            title='index_sdxml.zip',
            container=self.context)

        with open(out_fn, 'rb') as fp:
            obj.file = namedfile.NamedBlobImage(
                fp.read(),
                filename=u'index_sdxml.zip',
                contentType='application/zip')
        os.unlink(out_fn)
        return self.request.response.redirect(self.context.absolute_url())

    def convert_html(self):

        alsoProvides(self.request, IDisableCSRFProtection)

        sd = self.smashdocs

        out_fn = sd.export_document(
            document_id=self.context.document_id,
            format='html',
            user_id=self.user_id)

        if 'index_html.zip' in self.context.objectIds():
            plone.api.content.delete(self.context['index_html.zip'])

        obj = plone.api.content.create(
            type='File',
            id='index_html.zip',
            title='index_html.zip',
            container=self.context)

        with open(out_fn, 'rb') as fp:
            obj.file = namedfile.NamedBlobImage(
                fp.read(),
                filename=u'index_html.zip',
                contentType='application/zip')

        # unpack packed HTML ZIP file

        # create 'html' subfolder
        if 'html' in self.context.objectIds():
            plone.api.content.delete(self.context['html'])
        folder = plone.api.content.create(
            container=self.context,
            type='Folder',
            id='html',
            title='html')

        # create 'images' subfolder
        images = plone.api.content.create(
            container=folder,
            type='Folder',
            id='images',
            title='images')

        # unpack ZIP file
        with zipfile.ZipFile(out_fn, 'r') as zf:
            for name in zf.namelist():
                data = zf.read(name)
                if name == 'index.html':
                    obj = plone.api.content.create(
                        type='Document',
                        container=folder,
                        title='index.html',
                        id='index.html')
                    obj.text = RichTextValue(data, 'text/html', 'text/html')
                else:
                    basename = os.path.basename(name)
                    obj = plone.api.content.create(
                        type='Image',
                        id=basename,
                        title=basename,
                        container=images)
                    obj.image = namedfile.NamedBlobImage(
                        data,
                        filename=safe_unicode(basename),
                        contentType='image/png')

        os.unlink(out_fn)
        return self.request.response.redirect(self.context.absolute_url())
