# 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 re
import json
import urlparse

from django.db import models
from django.core.cache import cache
from django.core.urlresolvers import reverse
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.http import HttpResponse

from passerelle.base.models import BaseResource

import conciliation, suivi, integration

HEADERS = {
    'Accept': 'application/json',
    'Content-Type': 'application/json'
}

APPLICATION = 'AsgTeleprocedureApa14'


class SolisAPA(BaseResource):
    base_url = models.CharField(max_length=128, blank=False,
            verbose_name=_('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='solis_apa', null=True, blank=True,
            verbose_name=_('Keystore'),
            help_text=_('Certificate and private key in PEM format'))

    category = _('Business Process Connectors')

    class Meta:
        verbose_name = _('Solis')

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

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

    def _check_requests_response(self, response):
        try:
            ret = response.json()
            return ret
        except(ValueError) as e:
            raise Exception('Response content is not a valid JSON')

    def get_resource_url(self, uri):
        return urlparse.urljoin(self.base_url,uri)

    # Referentials methods
    def _referential(self, referential, keys=True, order_by=False,
            stop_on_error=False, attributes=[], **filters):
        uri = 'referential?referential=%s' % referential
        url = self.get_resource_url(uri)
        data = {
            'ReferentialOptions': {
                'processKeys': keys,
                'processOrderBy': order_by,
                'stopOnError': stop_on_error,
            }}
        if filters:
            solis_filters = []
            for k,v in filters.items():
                solis_filters.append({
                    'key':k,
                    'value': v
                    })
            data['ReferentialOptions']['Filters'] = {'Filter': solis_filters}
        if attributes:
            data['ReferentialOptions']['Attributes'] = {
                    "referential": [
                        {
                            "schema": "stdr",
                            "table": referential,
                            "field": attributes,
                            }
                        ]
                    }
        data = json.dumps(data)

        response = self.requests.post(url, data=data, headers=HEADERS)
        if response.status_code != 200:
            raise ValueError('referential ws: error code %d' % response.status_code)

        ret = self._check_requests_response(response)

        l = []

        count = int(ret['ReferentialOutputWS']['Entries']['@count'])

        name = ret['ReferentialOutputWS']['Entries']['@name']
        if count:
            entries = ret['ReferentialOutputWS']['Entries']['Entry']
            if type(entries) is not list:
                entries = [entries]
            l += entries
        return {'results': l, 'name': name, 'count': count, 'error': False}

    def _conciliation(self, config, **data):
        uri = 'conciliation'
        url = self.get_resource_url(uri)
        name = config['block']['name'].lower()
        data = json.dumps(conciliation.conciliation_payload(config, **data))
        response = self.requests.post(url, data=data, headers=HEADERS)
        if response.status_code != 200:
            raise ValueError('conciliation ws: error code %d' % response.status_code)

        ret = self._check_requests_response(response)

        l = []
        count = int(ret['ConciliationOutputWS']['Results']['@count'])
        if count:
            results = ret['ConciliationOutputWS']['Results']['ResultsByAffinity']
            if type(results) is not list:
                results = [results]
            for r in results:
                affinity = r['@affinity']
                entities = r['Entities']['entity']
                if type(entities) is not list:
                    entities = [entities]
                for e in entities:
                    e = conciliation.conciliation_output2dict(config, e)
                    e['@affinity'] = affinity
                    l.append(e)
        return {'results': l, 'name': name, 'count': count, 'error': False}

    def get_communes(self, query, code_dep=14):

        if query:
            query = query.lower()
            if re.match('^\d\d', query):
                # query est le debut d'un code postal
                code_dep = query[:2]
        if not code_dep:
            return {}

        cache_key = 'solis-liste-communes-%s' % code_dep
        ref = cache.get(cache_key)
        if not ref:
            ref = self._referential(referential='commune',
                attributes=['cp_lieu'],
                code_dep=code_dep)
            cache.set(cache_key, ref, 60*60)
        villes = ref.get('results')

        ret = []

        for v in villes:
            # {u'Attributes': {u'Attribute': {u'id': u'stdr.commune.cp_lieu',
            # u'value': 14210}}, u'Keys': {u'Key': [{u'id':
            # u'stdr.commune.code_a_com', u'value': 771}, {u'id':
            # u'stdr.commune.code_dep', u'value': 14}]}, u'id':
            # u'commune-14-771', u'value': u'NEUILLY LE MALHERBE'},
            attrs = {}
            for attr in v['Attributes']['Attribute']:
                attrs[attr['id']] = attr['value']
            text = '%0.5d %s' % (attrs['stdr.commune.cp_lieu'], v['value'].strip())

            if query and not query in text.lower():
                continue
            ret.append({'id': v['id'], 'text': text})
        return ret

    def _cache(self, key, value=None):
        if value:
            cache.set(key, value, 60*60)
            return True
        cache_data = cache.get(key)
        if cache_data:
            return cache_data

    def get_lieux(self, q, commune, departement):
        # si commune est un code solis de la forme commune-dep-com
        if commune and commune.startswith('commune-'):
            x, departement, commune = commune.split('-')
        call = self._conciliation(conciliation.CONCILIATION_ADRESSE,
                commune=commune, departement=departement,
                lieu='%%%s%%' % q)
        lieux = call.get('results')
        ret = []
        for l in lieux:
            # '@affinity': u'5',
            # 'CodeDepartement/@V': u'',
            # 'CodeLieu/@V': u'0110',
            # 'CodePostal/@V': u'14000',
            # 'Commune/NomCom/@V': u'CAEN',
            # 'Commune/PK/CodeCommune/@V': u'118',
            # 'NatureLieu/@Lc': u'RUE',
            # 'NomLieu/@V': u'DU BEAU SITE'
            for k,v in l.items():
                l[k] = v.strip()
            ret.append({
                'id': '%(CodeLieu/@V)s' % l,
                'text': '%(NatureLieu/@Lc)s %(NomLieu/@V)s' % l,
                'affinity': '%(@affinity)s' % l,
                })
        return ret

    def get_homonymes(self, nom, prenom, dn):
        if dn:
            dn = dn[6:]+'-'+dn[3:5]+'-'+dn[:2]
            call = self._conciliation(conciliation.CONCILIATION_INDIVIDU,
                    nom=nom, prenom=prenom, dn=dn)
        else:
            call = self._conciliation(conciliation.CONCILIATION_INDIVIDU_SANS_DN,
                    nom=nom, prenom=prenom)
        individus = call.get('results')
        ret = []
        for i in individus:
            # i = {'@affinity': u'3',
            #  'Dossier/Adresse/Commune/NomCom/@V': u'ST JULIEN EN GENEVOIS',
            #  'Dossier/Adresse/ComplementLieu/@V': u'ROUTE DE THOIRY',
            #  'Dossier/Adresse/CpLieu/@V': u'74160',
            #  'Dossier/Adresse/NatureLieu/@Lc': u'',
            #  'Dossier/Adresse/NomLieu/@V': u'.',
            #  'Dossier/Adresse/NumeroLieu/@V': u'39',
            #  'Dossier/PK/IndexDossier/@V': u'162438',
            #  'EtatCivil/DateNaissance/@V': u'1933-08-28',
            #  'EtatCivil/Nom/@V': u'DUPONT',
            #  'EtatCivil/NomJeuneFille/@V': u'BUATHIER',
            #  'EtatCivil/Prenom/@V': u'JEANNE',
            #  'PK/IndexIndividu/@V': u'208359'},
            for k,v in i.items():
                i[k] = v.strip()
            njf = i['EtatCivil/NomJeuneFille/@V']
            if njf:
                i['EtatCivil/NomJeuneFille/@V'] = u' (%s)' % njf
            if not i['EtatCivil/DateNaissance/@V']:
                i['EtatCivil/DateNaissance/@V'] = u'date de naissance inconnue'
            ret.append({
                'id': '%(PK/IndexIndividu/@V)s' % i,
                'text': ('%(EtatCivil/Nom/@V)s%(EtatCivil/NomJeuneFille/@V)s %(EtatCivil/Prenom/@V)s' + \
                        ' - %(EtatCivil/DateNaissance/@V)s' + \
                        ' - %(Dossier/Adresse/CpLieu/@V)s %(Dossier/Adresse/Commune/NomCom/@V)s') % i,
                'affinity': '%(@affinity)s' % i,
                })
        ret.sort(lambda x,y: cmp(y['affinity'],x['affinity']))
        return ret

    def _process_common_ref(self, ref_name, q=None):
        cache_key = 'solis-apa-%s' %ref_name.replace(' ','-')
        ref = self._cache(cache_key)
        if not ref:
            ref = self._referential(ref_name)
            self._cache(cache_key, ref)

        ret = []

        for result in ref.get('results'):
            if not q or q.lower() in result['value'].lower():
                ret.append({'id': result['id'], 'text': result['value']})

        return ret

    def get_referential(self, reference_name, q=None):
        return self._process_common_ref(reference_name.replace('-',' '), q=q)

    def get_suivi(self, suivi_type, datedebut, datefin):
        resource = {
            'visite': 'ExportSuiviVisite',
            'plan-aide': 'ExportSuiviPlanAide',
            'presentation-commission': 'ExportSuiviPresentationCommission',
            'decision-commission': 'ExportSuiviDecisionCommission'
        }

        uri = 'exportFlow?flow={}&application={}'.format(resource[suivi_type],
                APPLICATION)

        url = self.get_resource_url(uri)

        payload = suivi.render_payload(suivi_type, datedebut, datefin)
        payload = json.dumps(payload)
        response = self.requests.post(url, data=payload, headers=HEADERS)
        if response.status_code != 200:
            raise ValueError('suivi %s ws: error code %d' %(suivi_type, response.status_code))

        response = self._check_requests_response(response)

        output = suivi.suivi_output(suivi_type, response)
        return output

    def import_flow(self, data):
        uri = 'importFlow?flow=ImportIntegrationDemande&application=%s' %APPLICATION
        url = self.get_resource_url(uri)

        data = {'ImportInputWSDemandeApa': integration.build_message(json.loads(data))}
        data = json.dumps(data)
        self.logger.debug('Demande APA: %s' % data, extra={'solis_apa_demande': data})
        response = self.requests.post(url, data=data, headers=HEADERS)
        if response.status_code != 200:
            raise ValueError('integration ws: error code %d' %(response.status_code))

        response = self._check_requests_response(response)

        ret = {}

        for x in response['ImportIdResults']['Items']:
            ret[x['key']] = x['value']

        return ret
