# Passerelle - uniform access to data and services
# Copyright (C) 2019  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 xmlschema
from django.db import models
from django.utils.translation import gettext_lazy as _

from passerelle.utils.api import endpoint
from passerelle.utils.xml import JSONSchemaFromXMLSchema

from . import abstract


class DDPACSSchema(JSONSchemaFromXMLSchema):
    type_map = {
        'CiviliteType': 'civilite',
        '{http://www.w3.org/2001/XMLSchema}double': 'double',
    }
    civilite_map = {
        'Monsieur': 'M',
        'Madame': 'MME',
    }

    @classmethod
    def schema_civilite(cls):
        return {
            'type': 'string',
            'enum': ['Madame', 'Monsieur'],
        }

    @classmethod
    def schema_double(cls):
        return {
            'anyOf': [
                {'type': 'number'},
                {
                    'type': 'string',
                    'pattern': r'[0-9]*(\.[0-9]*)?',
                },
            ]
        }

    def encode_civilite(self, obj):
        try:
            return self.civilite_map[obj]
        except KeyError:
            raise xmlschema.XMLSchemaValidationError(self, obj, reason='civilite invalide')

    def decode_civilite(self, data):
        for key, value in self.civilite_map.items():
            if data.text == value:
                return xmlschema.ElementData(
                    tag=data.tag, text=key, content=data.content, attributes=data.attributes
                )
        raise xmlschema.XMLSchemaValidationError(self, data, reason='civilite invalide %s')

    def decode_double(self, data):
        return data._replace(text=str(data.text))

    def encode_double(self, obj):
        try:
            return float(obj)
        except ValueError:
            return 0.0


class Resource(abstract.Resource):
    category = _('Civil Status Connectors')
    xsd_root_element = 'PACS'
    flow_type = 'depotDossierPACS'
    doc_type = 'flux-pacs'
    xmlschema_class = DDPACSSchema

    class Meta:
        verbose_name = _('PACS request (MDEL DDPACS)')

    @endpoint(
        perm='can_access',
        methods=['post'],
        description=_('Create request'),
        post={'request_body': {'schema': {'application/json': None}}},
    )
    def create(self, request, post_data):
        return self._handle_create(request, post_data)

    def pre_process_create(self, data):
        def helper(d):
            if not isinstance(d, dict):
                return
            # choose between conventionType and conventionSpecifique
            if 'conventionSpecifique' in d:
                if d['conventionSpecifique']:
                    d.pop('conventionType', None)
                else:
                    del d['conventionSpecifique']
            # choose between filiationInconnu and filiationConnu
            if 'filiationInconnu' in d:
                if d['filiationInconnu']:
                    d.pop('filiationConnu', None)
                else:
                    del d['filiationInconnu']
            # convert codeNationalite to array of strings
            if isinstance(d.get('codeNationalite'), str):
                d['codeNationalite'] = [d['codeNationalite']]
            for key in d:
                if key in ('anneeNaissance', 'jourNaissance', 'moisNaissance'):
                    d[key] = int(d[key])
            for key in d:
                helper(d[key])

        helper(data)


Resource.create.endpoint_info.post['request_body']['schema'][
    'application/json'
] = Resource.get_create_schema()


class Demand(abstract.Demand):
    resource = models.ForeignKey(Resource, on_delete=models.CASCADE)

    class Meta:
        verbose_name = _('MDEL compatible DDPACS request')
