# passerelle.contrib.maarch
# Copyright (C) 2015  Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import json
import urlparse
import requests
from datetime import datetime
import logging

from django.views.generic import DetailView as GenericDetailView
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt

from passerelle import utils

from .soap import get_client, client_to_jsondict, recursive_asdict
from .models import Maarch

logger = logging.getLogger('passerelle.contrib.maarch')

class MaarchException(Exception):
    pass

class MaarchDetailView(GenericDetailView):
    model = Maarch
    template_name = 'passerelle/contrib/maarch/detail.html'


class DetailView(GenericDetailView):
    model = Maarch

    def get_client(self):
        return get_client(self.get_object())

    def get_data(self, request, *args, **kwargs):
        raise NotImplementedError

    @utils.protected_api('can_access')
    def get(self, request, *args, **kwargs):
        data = self.get_data(request, *args, **kwargs)
        return utils.response_for_json(request, data)


class PingView(DetailView):
    def get_data(self, request, *args, **kwargs):
        client = self.get_client()
        res = {'ping': 'pong'}
        if 'debug' in request.GET:
            res['client'] = client_to_jsondict(client)
        return res


class ResourceView(DetailView):
    @method_decorator(csrf_exempt)
    def dispatch(self, *args, **kwargs):
        return super(ResourceView, self).dispatch(*args, **kwargs)

    @utils.protected_api('can_access')
    def post(self, request, *args, **kwargs):
        client = self.get_client()
        formdata = json.loads(request.body)
        extras = formdata.get('extra', {})

        debug = 'debug' in request.GET
        if debug:
            debug_output = {}

        # get formdef schema from wcs API
        url = formdata['url']
        p = urlparse.urlsplit(url)
        scheme, netloc, path, query, fragment = \
            p.scheme, p.netloc, p.path, p.query, p.fragment
        schema_path = path.rsplit('/', 2)[0] + '/schema'
        schema_url = urlparse.urlunsplit((scheme, netloc, schema_path, query, fragment))
        schema = requests.get(schema_url).json()

        # storeAttachmentResource attachments: list, build from formdata file fields
        attachments = []

        #
        # build document (encodedFile and fileFormat)
        #

        document = u'<html>'
        document += u'<head><meta charset="utf-8"></head>'
        document += '<body>'
        document += u'<h1>%s</h1>' % schema['name']
        page = ''
        empty_page = True
        for field in schema['fields']:
            if field['type'] == 'page':
                # add last page, if it contains values
                if page and not empty_page:
                    document += page
                page = u'<hr /><h2>%s</h2>' % field['label']
                empty_page = True
            elif field['type'] == 'title':
                page += u'<h3>%s</h3>' % field['label']
            elif 'varname' in field:
                varname = field['varname']
                value = formdata['fields'].get(varname)
                if not value:
                    continue
                if field['type'] == 'file':
                    # field is a file: add it to attachments list
                    value['fileFormat'] = value['content_type'].split('/')[1] # FIXME (how ?)
                    attachments.append(value)
                    value = '%s' % value['filename']
                page += u'<dl>'
                page += u'<dt>%s</dt>' % field['label']
                page += u'<dd>%s</dd>' % value
                page += u'</dl>'
                empty_page = False
        if page and not empty_page: # add last page, if it contains values
            document += page
        document += u'</body></html>'
        encodedFile = document.encode('utf-8').encode('base64')
        fileFormat = 'html'

        if debug:
            debug_output['document'] = document

        #
        # build metadata for storeResource and storeExtResource
        #

        # storeResource metadata
        metadata = {}
        # storeExtResource metadata
        ext_metadata = {}

        # extract metadata and ext_metadata from formdata['extra']
        for name, value in extras.items():
            if name.startswith('maarch_metadata_'):
                metadata[name[16:]] = value
            if name.startswith('maarch_ext_metadata_'):
                ext_metadata[name[20:]] = value

        # prepare metadata for SOAP call
        def prepare_soap_metadata(metadata):
            datas = []
            for name, value in metadata.items():
                data = client.factory.create('arrayOfDataContent')
                data.column = name
                if name.endswith('_date'):
                    data.type = 'date'
                    value = datetime.strptime(value[:19], '%Y-%m-%dT%H:%M:%S')
                    value = value.strftime('%d-%m-%Y %H:%M:%S')
                elif isinstance(value, basestring):
                    data.type = 'string'
                elif isinstance(value, int):
                    data.type = 'int'
                elif isinstance(value, float):
                    data.type = 'float'
                elif value is None:
                    data.type = 'string'
                    value = ''
                data.value = value
                datas.append(data)
            soap_metadata = client.factory.create('arrayOfData')
            soap_metadata.datas = datas
            return soap_metadata

        metadata = prepare_soap_metadata(metadata)
        ext_metadata = prepare_soap_metadata(ext_metadata)

        if debug:
            debug_output['metadata'] = '%r' % metadata
            debug_output['ext_metadata'] = '%r' % ext_metadata

        #
        # get other Maarch variables (letterbox configuration by default)
        #

        collId = extras.get('maarch_collId') or 'letterbox_coll'
        table = extras.get('maarch_table') or 'res_letterbox'
        status = extras.get('maarch_status') or 'ATT'
        ext_table = extras.get('maarch_ext_table') or 'mlb_coll_ext'

        if debug:
            debug_output['collId'] = collId
            debug_output['table'] = table
            debug_output['status'] = status
            debug_output['ext_table'] = ext_table

        # if INES ESB (Tibco) between passerelle and Maarch, add a "maarch_id"
        # parameter. Get value from formdata or workflow options.
        maarch_id = extras.get('maarch_id')

        if debug:
            debug_output['maarch_id'] = maarch_id

        #
        # call Maarch web services
        #
        logger.debug('storeResource+Ext+Attachment: start')

        # store the resource (storeResource)
        logger.debug('storeResource: encodedFile(size):%r fileFormat:%r '
                     'collId:%r table:%r status:%r', len(encodedFile),
                        fileFormat, collId, table, status)
        logger.debug('storeResource: metadata: %r', metadata)
        if maarch_id:
            logger.debug('storeResource: INES maarch_id: %r', maarch_id)
            results = client.service.storeResource(
                    maarch_id,
                    encodedFile, metadata, collId,
                    table, fileFormat, status)
        else:
            results = client.service.storeResource(
                    encodedFile, metadata, collId,
                    table, fileFormat, status)
        data = recursive_asdict(results)
        logger.debug('storeResource result: %r', data)

        resId = data.get('resId')
        if not resId:
            raise MaarchException('no resId after storeResource')
        logger.debug('storeResource result: resId:%r', resId)

        logger.debug('storeExtResource: resId:%r ext_table:%r', resId,
                     ext_table)
        logger.debug('storeExtResource: ext_metadata: %r', ext_metadata)
        # store external metadata (storeExtResource)
        if maarch_id:
            logger.debug('storeExtResource: INES maarch_id: %r', maarch_id)
            results = client.service.storeExtResource(
                    maarch_id,
                    resId, ext_metadata, ext_table)
        else:
            results = client.service.storeExtResource(
                    resId, ext_metadata, ext_table)

        # store attachments
        for attachment in attachments:
            logger.debug('storeAttachmentResource: resId:%r collId:%r '
                         'content(size):%r fileFormat:%r filename:%r', resId,
                             collId, len(attachment['content']),
                             attachment['fileFormat'], attachment['filename'])
            if maarch_id:
                logger.debug('storeAttachmentResource: INES maarch_id: %r', maarch_id)
                client.service.storeAttachmentResource(
                        maarch_id,
                        resId, collId, attachment['content'],
                        attachment['fileFormat'], attachment['filename'])
            else:
                client.service.storeAttachmentResource(
                        resId, collId, attachment['content'],
                        attachment['fileFormat'], attachment['filename'])

        if debug:
            data['debug'] = debug_output

        logger.debug('storeResource+Ext+Attachment: resId:%r -- end', resId)
        return utils.response_for_json(request, data)
