# 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 base64
from urllib import parse as urlparse

import zeep
from django.db import models
from django.http import Http404
from django.utils.translation import gettext_lazy as _
from zeep.helpers import serialize_object

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

ASSOCIATION_SCHEMA = {
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "AstreGS assocation",
    "description": "",
    "type": "object",
    "required": [
        "Financier",
        "CodeFamille",
        "CatTiers",
        "NomEnregistrement",
        "StatutTiers",
        "Type",
        "AdresseTitre",
        "AdresseIsAdresseDeCommande",
        "AdresseIsAdresseDeFacturation",
    ],
    "properties": {
        "Financier": {"description": "financial association", "type": "string", "enum": ["true", "false"]},
        "CodeFamille": {
            "description": "association family code",
            "type": "string",
        },
        "CatTiers": {
            "description": "association category",
            "type": "string",
        },
        "NomEnregistrement": {
            "description": "association name",
            "type": "string",
        },
        "StatutTiers": {
            "description": "association status",
            "type": "string",
            "enum": ["PROPOSE", "VALIDE", "REFUSE", "BLOQUE", "A COMPLETER"],
        },
        "Type": {"description": "association type", "type": "string", "enum": ["D", "F", "*"]},
        "NumeroSiret": {
            "description": "SIREN number",
            "type": "string",
        },
        "NumeroSiretFin": {
            "description": "NIC number",
            "type": "string",
        },
        "AdresseTitre": {
            "type": "string",
        },
        "AdresseIsAdresseDeCommande": {"type": "string", "enum": ["true", "false"]},
        "AdresseIsAdresseDeFacturation": {"type": "string", "enum": ["true", "false"]},
        "organism": {
            "description": _('Organisme'),
            "type": "string",
        },
        "budget": {
            "description": _('Budget'),
            "type": "string",
        },
        "exercice": {
            "description": _('Exercice'),
            "type": "string",
        },
    },
}

CONTACT_SCHEMA = {
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "AstreGS contact",
    "description": "",
    "type": "object",
    "required": [
        "CodeContact",
        "CodeTitreCivilite",
        "Nom",
        "AdresseDestinataire",
        "CodePostal",
        "Ville",
        "EncodeKeyStatut",
    ],
    "properties": {
        "CodeContact": {
            "type": "string",
        },
        "CodeTitreCivilite": {
            "type": "string",
        },
        "Nom": {
            "type": "string",
        },
        "AdresseDestinataire": {
            "type": "string",
        },
        "CodePostal": {
            "type": "string",
        },
        "Ville": {
            "type": "string",
        },
        "EncodeKeyStatut": {
            "type": "string",
        },
        "organism": {
            "description": _('Organisme'),
            "type": "string",
        },
        "budget": {
            "description": _('Budget'),
            "type": "string",
        },
        "exercice": {
            "description": _('Exercice'),
            "type": "string",
        },
    },
}

DOCUMENT_SCHEMA = {
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "AstreGS assocation",
    "description": "",
    "type": "object",
    "required": [
        "Sujet",
        "Entite",
        "CodType",
        "Type",
        "hdnCodeTrt",
        "EncodeKeyEntite",
        "CodeDomaine",
        "CodDom",
        "document",
    ],
    "properties": {
        "Sujet": {
            "type": "string",
        },
        "Entite": {
            "type": "string",
        },
        "CodType": {
            "type": "string",
        },
        "Type": {
            "type": "string",
        },
        "hdnCodeTrt": {
            "type": "string",
        },
        "EncodeKeyEntite": {
            "type": "string",
        },
        "CodeDomaine": {
            "type": "string",
        },
        "CodDom": {
            "type": "string",
        },
        "document": {
            "type": "object",
            "required": ['filename', 'content_type', 'content'],
            'properties': {
                'filename': {
                    'type': 'string',
                },
                'content_type': {
                    'type': 'string',
                },
                'content': {
                    'type': 'string',
                },
            },
        },
        "organism": {
            "description": _('Organisme'),
            "type": "string",
        },
        "budget": {
            "description": _('Budget'),
            "type": "string",
        },
        "exercice": {
            "description": _('Exercice'),
            "type": "string",
        },
    },
}

GRANT_SCHEMA = {
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "AstreGS grant",
    "description": "",
    "type": "object",
    "required": [
        "Libelle",
        "LibelleCourt",
        "ModGestion",
        "TypeAide",
        "Sens",
        "CodeTiersDem",
        "CodeServiceGestionnaire",
        "CodeServiceUtilisateur",
    ],
    "properties": {
        "Libelle": {
            "type": "string",
        },
        "LibelleCourt": {
            "type": "string",
        },
        "ModGestion": {"type": "string", "enum": ["1", "2", "3", "4"]},
        "TypeAide": {
            "type": "string",
        },
        "Sens": {
            "type": "string",
        },
        "CodeTiersDem": {
            "type": "string",
        },
        "CodeServiceGestionnaire": {
            "type": "string",
        },
        "CodeServiceUtilisateur": {
            "type": "string",
        },
        "organism": {
            "description": _('Organisme'),
            "type": "string",
        },
        "budget": {
            "description": _('Budget'),
            "type": "string",
        },
        "exercice": {
            "description": _('Exercice'),
            "type": "string",
        },
    },
}

INDANA_SCHEMA = {
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "AstreGS INDANA indicator",
    "description": "",
    "type": "object",
    "required": ["CodeDossier", "CodeInd_1", "AnneeInd_1", "ValInd_1"],
    "properties": {
        "CodeDossier": {
            "type": "string",
        },
        "CodeInd_1": {
            "type": "string",
        },
        "AnneeInd_1": {
            "type": "string",
        },
        "ValInd_1": {
            "type": "string",
        },
        "IndAide": {
            "type": "string",
        },
        "organism": {
            "description": _('Organisme'),
            "type": "string",
        },
        "budget": {
            "description": _('Budget'),
            "type": "string",
        },
        "exercice": {
            "description": _('Exercice'),
            "type": "string",
        },
    },
}

INDANA_KEY_SCHEMA = {
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "AstreGS INDANA indicator key",
    "description": "",
    "type": "object",
    "required": ["CodeDossier", "CodeInd_1", "AnneeInd_1"],
    "properties": {
        "CodeDossier": {
            "type": "string",
        },
        "CodeInd_1": {
            "type": "string",
        },
        "AnneeInd_1": {
            "type": "string",
        },
        "organism": {
            "description": _('Organisme'),
            "type": "string",
        },
        "budget": {
            "description": _('Budget'),
            "type": "string",
        },
        "exercice": {
            "description": _('Exercice'),
            "type": "string",
        },
    },
}


TIERS_RIB_SCHEMA = {
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "AstreGS TiersRib",
    "description": "TiersRib",
    "type": "object",
    "required": [
        "CodeTiers",
        "CodePaiement",
        "LibelleCourt",
        "NumeroIban",
        "CleIban",
        'CodeBic',
        "CodeDomiciliation",
        "CodeStatut",
        "CodeDevise",
        "CodeIso2Pays",
        "LibelleCompteEtranger",
    ],
    "properties": {
        "CodeDevise": {"type": "string"},
        "CodeDomiciliation": {"type": "string"},
        "CodeIso2Pays": {"type": "string"},
        "CodePaiement": {"type": "string"},
        "CodeStatut": {
            "type": "string",
            "enum": ["PROPOSE", "VALIDE", "REFUSE", "A COMPLETER", "BLOQUE", "EN MODIFICATION"],
        },
        "CodeTiers": {"type": "string"},
        "IndicateurRibDefaut": {"type": "string"},
        "LibelleCompteEtranger": {"type": "string"},
        "LibelleCourt": {"type": "string"},
        "NumeroIban": {"type": "string"},
        "CleIban": {"type": "string"},
        "CodeBic": {"type": "string"},
        "IdRib": {"type": "string"},
        "organism": {
            "description": _('Organisme'),
            "type": "string",
        },
        "budget": {
            "description": _('Budget'),
            "type": "string",
        },
        "exercice": {
            "description": _('Exercice'),
            "type": "string",
        },
    },
}

TIERS_RIB_UPDATE_SCHEMA = {
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "AstreGS TiersRib",
    "description": "TiersRib Update",
    "type": "object",
    "required": [
        "CodePaiement",
        "LibelleCourt",
        "NumeroIban",
        "CleIban",
        "CodeBic",
        "CodeDomiciliation",
        "CodeStatut",
        "CodeDevise",
        "CodeIso2Pays",
        "LibelleCompteEtranger",
    ],
    "properties": {
        "CodeDevise": {"type": "string"},
        "CodeDomiciliation": {"type": "string"},
        "CodeIso2Pays": {"type": "string"},
        "CodePaiement": {"type": "string"},
        "CodeStatut": {
            "type": "string",
            "enum": ["PROPOSE", "VALIDE", "REFUSE", "A COMPLETER", "BLOQUE", "EN MODIFICATION"],
        },
        "IndicateurRibDefaut": {"type": "string"},
        "LibelleCompteEtranger": {"type": "string"},
        "LibelleCourt": {"type": "string"},
        "NumeroIban": {"type": "string"},
        "CleIban": {"type": "string"},
        "CodeBic": {"type": "string"},
        "organism": {
            "description": _('Organisme'),
            "type": "string",
        },
        "budget": {
            "description": _('Budget'),
            "type": "string",
        },
        "exercice": {
            "description": _('Exercice'),
            "type": "string",
        },
    },
}


class AstreGS(BaseResource):
    wsdl_base_url = models.URLField(_('Webservices base URL'))
    username = models.CharField(_('Username'), max_length=32)
    password = models.CharField(_('Password'), max_length=32)
    organism = models.CharField('Organisme', max_length=32)
    budget = models.CharField('Budget', max_length=32)
    exercice = models.CharField('Exercice', max_length=32)

    category = _('Business Process Connectors')

    log_requests_errors = False

    class Meta:
        verbose_name = 'AstresGS'

    def check_status(self):
        response = self.requests.get(self.wsdl_base_url)
        response.raise_for_status()

    @property
    def authentication(self):
        return {'USERNOM': self.username, 'USERPWD': self.password}

    def _set_context(self, context_params):
        for param_name in ('organism', 'budget', 'exercice'):
            if param_name in context_params:
                setattr(self, param_name, context_params[param_name])

    @property
    def context(self):
        return {'Organisme': self.organism, 'Budget': self.budget, 'Exercice': self.exercice}

    def get_client(self, wsdl_name):
        url = urlparse.urljoin(self.wsdl_base_url, '%s?wsdl' % wsdl_name)
        client = self.soap_client(wsdl_url=url, transport_kwargs={'remove_first_bytes_for_xml': True})
        parsed_wsdl_address = urlparse.urlparse(client.service._binding_options['address'])
        parsed_real_address = urlparse.urlparse(self.wsdl_base_url)
        client.service._binding_options['address'] = urlparse.urlunparse(
            parsed_real_address[:2] + parsed_wsdl_address[2:]
        )
        return client

    def call(self, wsdl_name, service, *args, **kwargs):
        try:
            client = self.get_client(wsdl_name)
            operation = getattr(client.service, service)
            kwargs['Authentification'] = self.authentication
            kwargs['Contexte'] = self.context
            if service == 'liste':
                return operation(**kwargs)
            return operation(kwargs)
        except zeep.exceptions.Error as e:
            raise APIError(e)

    def search_tiers(self, criterias):
        return self.call('RechercheTiers', 'liste', Criteres=criterias)

    @endpoint(
        description=_('Find associations by SIREN number'),
        perm='can_access',
        parameters={
            'siren': {'description': _('SIREN Number'), 'example_value': '77567227216096'},
            'organism': {'description': _('Organisme'), 'example_value': 'NOMDEVILLE'},
            'budget': {'description': _('Budget'), 'example_value': '01'},
            'exercice': {'description': _('Exercice'), 'example_value': '2022'},
        },
    )
    def associations(self, request, siren, **kwargs):
        self._set_context(kwargs)
        r = self.call('RechercheTiersDetails', 'liste', Criteres={'siren': '%s*' % siren})
        data = []
        if r.liste:
            for item in r.liste.EnregRechercheTiersDetailsReturn:
                association_data = serialize_object(item)
                association_data['id'] = association_data['Numero_SIRET']
                association_data['text'] = '%(Numero_SIRET)s - %(Nom_enregistrement)s' % association_data
                association_data['code'] = association_data['Code_tiers']
                association_data['name'] = association_data['Nom_enregistrement']
                data.append(association_data)
        return {'data': data}

    @endpoint(
        description=_('Check if association exists by its SIRET number'),
        name='check-association-by-siret',
        perm='can_access',
        parameters={
            'siret': {'description': _('SIRET Number'), 'example_value': '7756722721609600014'},
            'organism': {'description': _('Organisme'), 'example_value': 'NOMDEVILLE'},
            'budget': {'description': _('Budget'), 'example_value': '01'},
            'exercice': {'description': _('Exercice'), 'example_value': '2022'},
        },
    )
    def check_association_by_siret(self, request, siret, **kwargs):
        self._set_context(kwargs)
        r = self.search_tiers({'siren': siret})
        if r.liste:
            return {'exists': True}
        return {'exists': False}

    @endpoint(
        name='get-association-link-means',
        description=_('Get association linking means'),
        perm='can_access',
        parameters={
            'association_id': {'description': _('Association ID'), 'example_value': '42435'},
            'NameID': {'description': _('Publik ID'), 'example_value': 'xyz24d934'},
            'organism': {'description': _('Organisme'), 'example_value': 'NOMDEVILLE'},
            'budget': {'description': _('Budget'), 'example_value': '01'},
            'exercice': {'description': _('Exercice'), 'example_value': '2022'},
        },
    )
    def get_association_link_means(self, request, association_id, NameID=None, **kwargs):
        self._set_context(kwargs)
        r = self.call('Tiers', 'Chargement', TiersCle={'CodeTiers': association_id})
        response = {'already_paired': False}
        if (
            NameID
            and Link.objects.filter(resource=self, name_id=NameID, association_id=association_id).exists()
        ):
            response['already_paired'] = True
        data = []
        # assocation contact is defined in EncodeKeyContact attribute
        if not r.EncodeKeyContact:
            return {'data': data}

        r = self.call('Contact', 'Chargement', ContactCle={'idContact': r.EncodeKeyContact})
        if r.AdresseMail:
            data.append(
                {
                    'id': 'email',
                    'text': 'par courriel vers %s***@***%s' % (r.AdresseMail[:2], r.AdresseMail[-3:]),
                    'value': r.AdresseMail,
                    'type': 'email',
                }
            )
        if r.TelephoneMobile:
            mobile = ''.join(n for n in r.TelephoneMobile if is_number(n))
            if mobile and len(mobile) == 10 and mobile[:2] in ('06', '07'):
                data.append(
                    {
                        'id': 'mobile',
                        'text': 'par SMS vers %s*****%s' % (mobile[:2], mobile[-3:]),
                        'value': mobile,
                        'type': 'mobile',
                    }
                )
        response['data'] = data
        response['raw_data'] = serialize_object(r)
        return response

    @endpoint(
        description=_('Create link between user and association'),
        perm='can_access',
        parameters={
            'NameID': {
                'description': _('Publik NameID'),
                'example_value': 'xyz24d934',
            },
            'association_id': {
                'description': _('Association ID'),
                'example_value': '12345',
            },
            'organism': {
                'description': _('Organisme'),
                'type': 'string',
            },
            'budget': {
                'description': _('Budget'),
                'type': 'string',
            },
            'exercice': {
                'description': _('Exercice'),
                'type': 'string',
            },
        },
    )
    def link(self, request, NameID, association_id, **kwargs):
        self._set_context(kwargs)
        link, created = Link.objects.get_or_create(
            resource=self, name_id=NameID, association_id=association_id
        )
        link.get_label()
        return {'link': link.id, 'created': created, 'association_id': link.association_id}

    @endpoint(
        description=_('Remove link between user and association'),
        perm='can_access',
        parameters={
            'NameID': {'description': _('Publik NameID'), 'example_value': 'xyz24d934'},
            'association_id': {'description': _('Association ID'), 'example_value': '12345'},
        },
    )
    def unlink(self, request, NameID, association_id):
        try:
            link = Link.objects.get(resource=self, name_id=NameID, association_id=association_id)
            link.delete()
            return {'deleted': True}
        except Link.DoesNotExist:
            raise Http404('link not found')

    @endpoint(
        description=_('List user links'),
        perm='can_access',
        parameters={
            'NameID': {
                'description': _('Publik NameID'),
                'example_value': 'xyz24d934',
            },
            'organism': {
                'description': _('Organisme'),
                'type': 'string',
            },
            'budget': {
                'description': _('Budget'),
                'type': 'string',
            },
            'exercice': {
                'description': _('Exercice'),
                'type': 'string',
            },
        },
    )
    def links(self, request, NameID, **kwargs):
        if not Link.objects.filter(resource=self, name_id=NameID).exists():
            return {'data': []}
        data = []
        for link in Link.objects.filter(resource=self, name_id=NameID):
            link.resource._set_context(kwargs)
            data.append({'id': link.association_id, 'text': link.get_label()})
        return {'data': data}

    @endpoint(
        perm='can_access',
        name='create-association',
        post={
            'description': _('Creates an association'),
            'request_body': {'schema': {'application/json': ASSOCIATION_SCHEMA}},
        },
    )
    def create_association(self, request, post_data):
        self._set_context(post_data)
        r = self.call('Tiers', 'Creation', Tiers=post_data)
        return {'data': serialize_object(r)}

    @endpoint(
        description=_('Get association informations'),
        name='get-association-by-id',
        perm='can_access',
        parameters={
            'association_id': {'description': _('Association ID'), 'example_value': '42435'},
            'NameID': {'description': _('Publik ID'), 'example_value': 'xyz24d934'},
            'organism': {'description': _('Organisme'), 'example_value': 'NOMDEVILLE'},
            'budget': {'description': _('Budget'), 'example_value': '01'},
            'exercice': {'description': _('Exercice'), 'example_value': '2022'},
        },
    )
    def get_association_by_id(self, request, association_id, NameID=None, **kwargs):
        self._set_context(kwargs)
        if (
            NameID
            and not Link.objects.filter(resource=self, name_id=NameID, association_id=association_id).exists()
        ):
            raise Http404('no association found')
        r = self.call('Tiers', 'Chargement', TiersCle={'CodeTiers': association_id})
        return {'data': serialize_object(r)}

    @endpoint(
        name='get-contact',
        perm='can_access',
        description=_('Get contact details'),
        parameters={
            'contact_id': {
                'description': _('Contact identifier'),
                'example_value': '1111',
            },
            'organism': {'description': _('Organisme'), 'example_value': 'NOMDEVILLE'},
            'budget': {'description': _('Budget'), 'example_value': '01'},
            'exercice': {'description': _('Exercice'), 'example_value': '2022'},
        },
    )
    def get_contact(self, request, contact_id, **kwargs):
        self._set_context(kwargs)
        r = self.call('Contact', 'Chargement', ContactCle={'idContact': contact_id})
        return {'data': serialize_object(r)}

    @endpoint(
        name='create-contact',
        perm='can_access',
        post={
            'description': _('Create contact'),
            'request_body': {'schema': {'application/json': CONTACT_SCHEMA}},
        },
    )
    def create_contact(self, request, post_data):
        self._set_context(post_data)
        contact = self.call('Contact', 'Creation', Contact=post_data)
        # address should be set separatedly
        post_data['EncodeKeyContact'] = contact.idContact
        self.call('ContactAdresses', 'Creation', ContactAdresses=post_data)
        return {'data': serialize_object(contact)}

    @endpoint(
        description=_('Delete contact'),
        name='delete-contact',
        perm='can_access',
        parameters={
            'contact_id': {'description': _('Contact ID'), 'example_value': '4242'},
            'organism': {'description': _('Organisme'), 'example_value': 'NOMDEVILLE'},
            'budget': {'description': _('Budget'), 'example_value': '01'},
            'exercice': {'description': _('Exercice'), 'example_value': '2022'},
        },
    )
    def delete_contact(self, request, contact_id, **kwargs):
        self._set_context(kwargs)
        r = self.call('Contact', 'Suppression', ContactCle={'idContact': contact_id})
        return {'data': serialize_object(r)}

    @endpoint(
        name='create-document',
        perm='can_access',
        post={
            'description': _('Create document'),
            'request_body': {'schema': {'application/json': DOCUMENT_SCHEMA}},
        },
    )
    def create_document(self, request, post_data):
        self._set_context(post_data)
        document = post_data.pop('document')
        post_data['NomFichier'] = document['filename']
        post_data['MimeType'] = document['content_type']
        post_data['Contenu'] = base64.b64decode(document['content'])
        r = self.call('DocumentAnnexe', 'Creation', DocumentAnnexe=post_data)
        return {'data': serialize_object(r)}

    @endpoint(
        name='create-grant-demand',
        perm='can_access',
        post={
            'description': _('Create grant demand'),
            'request_body': {'schema': {'application/json': GRANT_SCHEMA}},
        },
    )
    def create_grant_demand(self, request, post_data):
        self._set_context(post_data)
        r = self.call('Dossier', 'Creation', Dossier=post_data)
        return {'data': serialize_object(r)}

    @endpoint(
        name='create-indana-indicator',
        perm='can_access',
        post={
            'description': _('Create indana indicator'),
            'request_body': {'schema': {'application/json': INDANA_SCHEMA}},
        },
    )
    def create_indana_indicator(self, request, post_data):
        self._set_context(post_data)
        r = self.call('DossierIndicateur', 'Creation', DossierIndicateur=post_data)
        return {'data': serialize_object(r)}

    @endpoint(
        name='update-indana-indicator',
        perm='can_access',
        post={
            'description': _('Update indana indicator'),
            'request_body': {'schema': {'application/json': INDANA_SCHEMA}},
        },
    )
    def update_indana_indicator(self, request, post_data):
        self._set_context(post_data)
        r = self.call('DossierIndicateur', 'Modification', DossierIndicateur=post_data)
        return {'data': serialize_object(r)}

    @endpoint(
        name='delete-indana-indicator',
        perm='can_access',
        post={
            'description': _('Delete indana indicator'),
            'request_body': {'schema': {'application/json': INDANA_KEY_SCHEMA}},
        },
    )
    def delete_indana_indicator(self, request, post_data):
        self._set_context(post_data)
        r = self.call('DossierIndicateur', 'Suppression', DossierIndicateurCle=post_data)
        return {'data': serialize_object(r)}

    @endpoint(
        name='create-tiers-rib',
        perm='can_access',
        post={
            'description': _('Create RIB'),
            'request_body': {'schema': {'application/json': TIERS_RIB_SCHEMA}},
        },
    )
    def create_tiers_rib(self, request, post_data):
        self._set_context(post_data)
        r = self.call('TiersRib', 'Creation', TiersRib=post_data)
        return {'data': serialize_object(r)}

    @endpoint(
        name='get-tiers-rib',
        perm='can_access',
        description=_('Get RIB'),
        parameters={
            'CodeTiers': {'example_value': '42435'},
            'IdRib': {'example_value': '4242'},
            'organism': {'description': _('Organisme'), 'example_value': 'NOMDEVILLE'},
            'budget': {'description': _('Budget'), 'example_value': '01'},
            'exercice': {'description': _('Exercice'), 'example_value': '2022'},
        },
    )
    def get_tiers_rib(self, request, CodeTiers, IdRib, **kwargs):
        self._set_context(kwargs)
        payload = {'CodeTiers': CodeTiers, 'IdRib': IdRib}
        r = self.call('TiersRib', 'Chargement', TiersRibCle=payload)
        return {'data': serialize_object(r)}

    @endpoint(
        name='update-tiers-rib',
        perm='can_access',
        post={
            'description': _('Update RIB'),
            'request_body': {'schema': {'application/json': TIERS_RIB_UPDATE_SCHEMA}},
        },
        parameters={
            'CodeTiers': {'example_value': '42435'},
            'IdRib': {'example_value': '4242'},
        },
    )
    def update_tiers_rib(self, request, CodeTiers, IdRib, post_data):
        self._set_context(post_data)
        post_data['CodeTiers'] = CodeTiers
        post_data['IdRib'] = IdRib
        r = self.call('TiersRib', 'Modification', TiersRib=post_data)
        return {'data': serialize_object(r)}

    @endpoint(
        name='delete-tiers-rib',
        perm='can_access',
        description=_('Delete RIB'),
        parameters={
            'CodeTiers': {'example_value': '42435'},
            'IdRib': {'example_value': '4242'},
            'organism': {'description': _('Organisme'), 'example_value': 'NOMDEVILLE'},
            'budget': {'description': _('Budget'), 'example_value': '01'},
            'exercice': {'description': _('Exercice'), 'example_value': '2022'},
        },
    )
    def delete_tiers_rib(self, request, CodeTiers, IdRib, **kwargs):
        self._set_context(kwargs)
        payload = {'CodeTiers': CodeTiers, 'IdRib': IdRib}
        r = self.call('TiersRib', 'Suppression', TiersRibCle=payload)
        return {'data': serialize_object(r)}

    @endpoint(
        name='find-tiers-by-rib',
        perm='can_access',
        description=_('Find person by RIB'),
        parameters={
            'banque': {'example_value': '30001'},
            'guichet': {'example_value': '00794'},
            'numero_compte': {'example_value': '12345678901'},
            'cle': {'example_value': '85'},
            'organism': {'description': _('Organisme'), 'example_value': 'NOMDEVILLE'},
            'budget': {'description': _('Budget'), 'example_value': '01'},
            'exercice': {'description': _('Exercice'), 'example_value': '2022'},
        },
    )
    def find_tiers_by_rib(self, request, banque, guichet, numero_compte, cle, **kwargs):
        self._set_context(kwargs)
        criteres = {'banque': banque, 'guichet': guichet, 'numeroCompte': numero_compte, 'cleRIB': cle}
        # add other params to search criterias
        criteres.update(kwargs)
        r = self.search_tiers(criteres)
        data = []
        if r.liste:
            for item in r.liste.EnregRechercheTiersReturn:
                tiers_data = serialize_object(item)
                tiers_data['id'] = tiers_data['N']
                tiers_data['text'] = '%{Nom_Enregistrement}s (%{N}s)'.format(**tiers_data)
                data.append(tiers_data)
        return {'data': data}

    @endpoint(
        name='get-dossier',
        perm='can_access',
        description=_('Get Dossier'),
        parameters={
            'CodeDossier': {'example_value': '2021-0004933'},
            'organism': {'description': _('Organisme'), 'example_value': 'NOMDEVILLE'},
            'budget': {'description': _('Budget'), 'example_value': '01'},
            'exercice': {'description': _('Exercice'), 'example_value': '2022'},
        },
    )
    def get_dossier(self, request, CodeDossier, **kwargs):
        self._set_context(kwargs)
        payload = {'CodeDossier': CodeDossier}
        r = self.call('Dossier', 'Chargement', DossierCle=payload)
        return {'data': serialize_object(r)}


class Link(models.Model):
    resource = models.ForeignKey(AstreGS, on_delete=models.CASCADE)
    name_id = models.CharField(max_length=32)
    association_id = models.CharField(max_length=32)
    association_label = models.CharField(max_length=128, null=True)
    created = models.DateTimeField(auto_now_add=True)

    def get_label(self):
        r = self.resource.call(
            'RechercheTiersDetails', 'liste', Criteres={'numeroTiers': self.association_id}
        )
        if r.liste:
            # ugly API, it always returns a list of only one element
            item = r.liste.EnregRechercheTiersDetailsReturn[0]
            self.association_label = '%s - %s' % (item.Numero_SIRET, item.Nom_enregistrement)
            self.save()
        else:
            self.resource.logger.error('No data found for association %s', self.association_id)
        return self.association_label

    class Meta:
        unique_together = ('resource', 'name_id', 'association_id')
