# Passerelle - uniform access to data and services
# 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 base64
import json
import magic

from requests.exceptions import ConnectionError
from django.db import models
from django.core.urlresolvers import reverse
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.http import HttpResponse, Http404

from passerelle.base.models import BaseResource
from passerelle.utils.api import endpoint
from passerelle.utils.jsonresponse import APIError

from .soap import get_client as soap_get_client


def get_client(model):
    try:
        return soap_get_client(model)
    except ConnectionError as e:
        raise APIError('i-Parapheur error: %s' % e)

def format_type(t):
    return {'id': unicode(t), 'text': unicode(t)}

def format_file(f):
    return {'status': f.status, 'id': f.nom, 'timestamp': f.timestamp}


class FileError(Exception):
    pass


class FileNotFoundError(Exception):
    http_status = 404


class IParapheur(BaseResource):
    wsdl_url = models.CharField(max_length=128, blank=False,
            verbose_name=_('WSDL URL'),
            help_text=_('WSDL URL'))
    verify_cert = models.BooleanField(default=True,
            verbose_name=_('Check HTTPS Certificate validity'))
    username = models.CharField(max_length=128, blank=True,
            verbose_name=_('Username'))
    password = models.CharField(max_length=128, blank=True,
            verbose_name=_('Password'))
    keystore = models.FileField(upload_to='iparapheur', null=True, blank=True,
            verbose_name=_('Keystore'),
            help_text=_('Certificate and private key in PEM format'))

    category = _('Business Process Connectors')

    class Meta:
        verbose_name = _('i-Parapheur')

    @classmethod
    def get_icon_class(cls):
        return 'ressources'

    @classmethod
    def get_verbose_name(cls):
        return cls._meta.verbose_name

    @endpoint(serializer_type='json-api', perm='can_access')
    def types(self, request):
        c = get_client(self)
        return [format_type(t) for t in c.service.GetListeTypes()]

    @endpoint(serializer_type='json-api', perm='can_access')
    def ping(self, request):
        c = get_client(self)
        return c.service.echo('ping')

    @endpoint(serializer_type='json-api', perm='can_access')
    def subtypes(self, request, type=None):
        c = get_client(self)
        if type:
            return [format_type(t) for t in c.service.GetListeSousTypes(type)]
        return [format_type(t) for t in c.service.GetListeSousTypes()]

    @endpoint(serializer_type='json-api', perm='can_access')
    def files(self, request, status=None):
        c = get_client(self)
        if status:
            return [format_file(f) for f in c.service.RechercherDossiers(Status=status)]
        return [format_file(f) for f in c.service.RechercherDossiers()]

    @endpoint(serializer_type='json-api', perm='can_access', name='create-file', methods=['post'])
    def create_file(self, request, email=None):
        data = json.loads(request.body)
        title = data['title']
        typ = data['type']
        subtyp = data['subtype']
        email = data.get('email')
        visibility = data['visibility']
        content = data['data']
        mime = magic.open(magic.MAGIC_MIME_TYPE)
        mime.load()
        content_type = data.get('content_type') if data.get('content_type') \
                else mime.buffer(base64.b64decode(data['data']))

        c = get_client(self)
        if visibility not in ['PUBLIC', 'SERVICE', 'CONFIDENTIEL']:
            raise FileError('Unknown value for "visibility". Should be "PUBLIC", "SERVICE" or "CONFIDENTIEL"')
        doc = c.factory.create("TypeDoc")
        doc['value'] = content
        doc['_contentType'] = content_type
        d = {'TypeTechnique': typ,
             'DossierTitre': title,
             'SousType': subtyp,
             'Visibilite': visibility,
             'DocumentPrincipal' : doc,
             }
        if email:
            d['EmailEmetteur'] = email
        r = c.service.CreerDossier(**d)
        if r.MessageRetour.codeRetour == 'KO':
            raise FileError(r.MessageRetour.message)
        return {'RecordId': r.DossierID,
                'message': r.MessageRetour.message}

    @endpoint(serializer_type='json-api', perm='can_access', name='get-file', pattern='(?P<file_id>[\w-]+)')
    def get_file(self, request, file_id):
        client = get_client(self)
        resp = client.service.GetDossier(file_id)
        if resp.MessageRetour.codeRetour == 'KO':
            if 'inconnu' in resp.MessageRetour.message:
                raise Http404(resp.MessageRetour.message)
            raise FileError(resp.MessageRetour.message)
        fichier_nom = resp.MetaDonnees.MetaDonnee[0]['valeur']
        fichier = resp.DocumentsAnnexes.DocAnnexe[0].fichier
        return HttpResponse(base64.b64decode(fichier['value']),
                content_type=fichier['_contentType'])

    @endpoint(serializer_type='json-api', perm='can_access', name='get-file-status', pattern='(?P<file_id>[\w-]+)')
    def get_file_status(self, request, file_id):
        c = get_client(self)
        resp = c.service.GetHistoDossier(file_id)
        if resp.MessageRetour.codeRetour == 'KO':
            if 'inconnu' in resp.MessageRetour.message:
                raise Http404(resp.MessageRetour.message)
            raise FileError(resp.MessageRetour.message)
        last = resp.LogDossier[-1]
        return {'annotation': last.annotation, 'nom': last.nom,
            'status': last.status, 'timestamp': last.timestamp
        }
