# © 2019 Le Filament (<http://www.le-filament.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

import requests
import logging
import xml.etree.ElementTree as et

from odoo import models, exceptions, fields
from cmislib.exceptions import ContentAlreadyExistsException, \
    ObjectNotFoundException, UpdateConflictException, InvalidArgumentException

_logger = logging.getLogger(__name__)


class CgscopAlfresco(models.AbstractModel):
    """ Appelle l'API alfresco et implémente les fonctions suivantes :

    alfresco_list_docs : liste les documents
    """
    _name = 'cgscop.alfresco'
    _description = 'Connecteur Alfresco'

    def alfresco_get_by_url(self, url, call_type, json=None):
        """ Création de la requête à Alfresco
        
        :param url: action API Alfresco
        :param call_type: post/get
        :param json: data to post

        @return response au format JSON
        """
        _logger.info("Calling %s" % url)
        param = self.env['cmis.backend'].sudo().search([
            ['active', '=', True]])
        if not param:
            raise exceptions.Warning(
                "La connexion avec Alfresco n'est pas configurée !")
            return False
        alfresco_url = param.url
        alfresco_ssl = param.ssl
        basicAuthCredentials = (
            param.username,
            param.password)
        try:
            if call_type == 'get':
                response = requests.get(
                    alfresco_url + url,
                    auth=basicAuthCredentials,
                    verify=alfresco_ssl)
            elif call_type == 'post':
                response = requests.post(
                    alfresco_url + url,
                    auth=basicAuthCredentials,
                    json=json,
                    verify=alfresco_ssl)
            elif call_type == 'delete':
                response = requests.delete(
                    alfresco_url + url,
                    auth=basicAuthCredentials,
                    json=json,
                    verify=alfresco_ssl)
        except Exception as err:
            _logger.warning(
                "Erreur de connexion. URL: %s",
                err.__str__(),
            )
            response = False

        # Gestion erreur API
        if response.status_code not in [200, 201]:
            try:
                message = response.json().get('message')
            except:
                message = response.text
            raise exceptions.Warning(
                "L'appel url '%s' a échoué\n"
                "- Code erreur : %d\n"
                "- Message : %s" % (
                    response.url,
                    response.status_code,
                    message))
        # Si dossier déjà créé
        elif (response.text.find("NUMADHERENT d") > 0 or
              response.text.find("SIRET d") > 0 or
              response.text.find("RAISONSOCIALE d") > 0):
            return response.json().get('dossiersExistants')[0]
        return response

    # Get Methods
    def alfresco_list_docs(self, node_id):
        """ Liste les documents pour un organisme (raison sociale)
            :param name: raison sociale de la structure
        """
        url = '/alfresco/s/erp/listedocuments?nodeId=' + node_id
        return self.alfresco_get_by_url(url=url, call_type='get').json()

    def alfresco_list_type(self):
        """ Liste le valeurs du formulaire de dépôt d'un nouveau document

        @return: dict des valeurs possibles pour période, validité, type
        """
        url = '/alfresco/s/api/properties?nsp=crm&n=document'
        list_type = self.alfresco_get_by_url(url=url, call_type='get').json()
        return {
            'periode': self.get_allowed_values(
                list_type=list_type,
                value='crm:periode'),
            'type': self.get_allowed_values(
                list_type=list_type,
                value='crm:type'),
        }

    def alfresco_get_doc(self, nodeRef):
        """ Retourne le contenu d'un document en base64
            :param nodeRef: id Alfresco
        """
        url = '/alfresco/s/document/' + nodeRef
        return self.alfresco_get_by_url(url=url, call_type='get').json()

    def alfresco_get_ticket(self, username):
        """ Liste les documents pour un organisme (raison sociale)
            :param username: username de l'utilisateur
        """
        url = '/alfresco/s/authentifier-entantque?compte=' + username
        result = self.alfresco_get_by_url(url=url, call_type='get')
        tree = et.ElementTree(et.fromstring(result.content))
        root = tree.getroot()
        return root[0].text

    # Post Methods
    def alfresco_create_organism(self, partner):
        """ Création d'un dossier Organisme

        :param partner: objet Odoo créé

        @return: id Alfresco (nodeRef) du dossier
        """
        url = '/alfresco/s/erp/createdossierorganisme'
        json = {
            'raisonSociale': partner.name,
            "id": partner.id,
            "siret": partner.siret,
            "numAdherent": partner.member_number,
            "ur": partner.ur_id
        }
        return self.get_nodeRef(
            self.alfresco_get_by_url(url=url, call_type='post', json=json).json())

    def alfresco_update_organism(self, partner):
        """ Mise à jour d'un dossier Organisme

        :param partner: objet Odoo créé

        @return: id Alfresco (nodeRef) du dossier
        """
        url = '/alfresco/s/erp/updatedossierorganisme/' + partner.id_alfresco
        json = {
            'raisonSociale': partner.name,
            "id": partner.id,
            "siret": partner.siret,
            "numAdherent": partner.member_number,
            "ur": partner.ur_id
        }
        return self.get_nodeRef(
            self.alfresco_get_by_url(url=url, call_type='post', json=json).json())

    def alfresco_upload(self, partner, type, periode, validite, filename, mimetype, doc):
        """ Upload d'un document sur Alfresco

        :param doc: document encodé en base64

        @return: id Alfresco (nodeRef) du fichier
        """
        url = '/alfresco/s/erp/depotdocument'
        json = {
            'folderNodeId': partner.id_alfresco,
            'periode': periode,
            'type': type,
            'nomOrigine': filename,
            'mimeType': mimetype,
            'contentBase64': doc,
        }
        return self.get_nodeRef(
            self.alfresco_get_by_url(url=url, call_type='post', json=json).json())

    # Delete Methods
    def alfresco_remove(self, nodeRef):
        """ Supprime un document d'Alfresco
            :param id_doc: id du document Alfresco
        """
        url = '/alfresco/s/document/' + nodeRef
        return self.alfresco_get_by_url(url=url, call_type='delete').json()

    # Others
    def get_allowed_values(self, list_type, value):
        """ Fonction qui itère dans l'objet retourné
        """
        list_selected = list(filter(
            lambda i: i['name'] == value,
            list_type))
        return list_selected[0].get(
            'constraints')[0].get('parameters')[0].get('allowedValues')

    def get_nodeRef(self, result):
        """ Renvoie la référence Alfresco d'un JSON contenant au premier
        niveau la clé "nodeRef" puis supprime le chemin du workspace
        """
        return result.get('nodeRef', '').replace(
            'workspace://SpacesStore/', '')

    def push_alfresco_file(
            self, file, name, partner, type,
            context='STD', year=fields.Datetime.now().year):
        """
            Ajoute un fichier sur la GED Alfresco
            @return: fonction get_partner_files() de res.partner
        """
        backend = self.env['cmis.backend'].search([], limit=1)

        if not partner.partner_cmis_folder:
            # TODO: commit value on creation because of error
            folder = partner.create_cmis_folder()
        else:
            folder = partner.partner_cmis_folder

        try:
            cmis_obj = backend.get_cmis_repository().getObject(folder)

            file_upload = cmis_obj.createDocument(
                name=name,
                properties={
                    'cmis:objectTypeId': 'D:crm:document',
                    'cmis:secondaryObjectTypeIds': ['P:crm:organisme'],
                    'crm:type': type,
                    'crm:contexte': context,
                    'crm:annee': year
                },
                contentFile=file,
                contentType='application/pdf'
            )
        except (ContentAlreadyExistsException, ObjectNotFoundException,
                UpdateConflictException, InvalidArgumentException) as e:
            _logger.error(e)
            raise exceptions.ValidationError("""Erreur de mise à jour Alfresco :
                - Code : %s
                - Url : %s
                - Détail : %s""" % (e.status, e.url, e.details))
        except Exception as e:
            raise exceptions.ValidationError(e)

        return file_upload