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

import logging
import xml.etree.ElementTree as et

import requests
from cmislib.exceptions import (
    ContentAlreadyExistsException,
    InvalidArgumentException,
    ObjectNotFoundException,
    UpdateConflictException,
)

from odoo import _, exceptions, models

_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, files=None):
        """Création de la requête à Alfresco

        :param url: action API Alfresco
        :param call_type: post/get
        :param json: data to post
        :param files: byte to upload
        :param data: 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.UserError(
                _("La connexion avec Alfresco n'est pas configurée !")
            )
        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,
                    timeout=15,
                )
            elif call_type == "post":
                response = requests.post(
                    alfresco_url + url,
                    auth=basicAuthCredentials,
                    json=json,
                    files=files,
                    verify=alfresco_ssl,
                    timeout=15,
                )
            elif call_type == "put":
                response = requests.put(
                    alfresco_url + url,
                    auth=basicAuthCredentials,
                    data=json,
                    files=files,
                    verify=alfresco_ssl,
                    timeout=15,
                )
            elif call_type == "delete":
                response = requests.delete(
                    alfresco_url + url,
                    auth=basicAuthCredentials,
                    json=json,
                    verify=alfresco_ssl,
                    timeout=15,
                )
        except Exception as err:
            _logger.warning(
                "Erreur de connexion. URL: %s",
                err.__str__(),
            )
            raise exceptions.ValidationError(err.__str__())

        # Gestion erreur API
        if response.status_code not in [200, 201]:
            try:
                message = response.json().get("message")
            except Exception:
                message = response.text
            raise exceptions.ValidationError(
                _(
                    "L'appel url '%s' a échoué\n"
                    "- Code erreur : %d\n"
                    "- Message : %s" % (response.url, response.status_code, message)
                )
            )

        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()
        )

    # 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, metadata=None, backend=None, folder=None):
        """
        Ajoute un fichier sur la GED Alfresco
        @return: fonction get_partner_files() de res.partner
        """
        if not backend:
            backend = self.env["cmis.backend"].search([], limit=1)

        if not folder:
            raise exceptions.ValidationError(
                _("Le dossier parent n'existe pas ou n'est pas renseigné !")
            )

        cmis_rep = backend.get_cmis_repository()
        cmis_obj = cmis_rep.getObject(folder)
        _logger.info("Chargement document Alfresco : %s" % name)
        try:
            file_upload = cmis_obj.createDocument(
                name=name,
                properties=metadata,
                contentFile=file,
                contentType="application/pdf",
            )
            return file_upload
        except UpdateConflictException:
            _logger.info("Le document %s existe déjà dans le dossier spécifié" % name)
            # Recherche noderef document existant
            children = cmis_obj.getChildren().getResults()
            old_cmis_file = list(filter(lambda f: f.name == name, children))[0]
            # Création d'une copie de travail du document pour versionnage
            pwc = old_cmis_file.checkout()
            # Mise à jour de la version
            pwc.checkin(
                contentFile=file, contentType="application/pdf", properties=metadata
            )
        except (
            ContentAlreadyExistsException,
            ObjectNotFoundException,
            InvalidArgumentException,
        ) as e:
            _logger.error(e)
            raise exceptions.ValidationError(
                _(
                    "L'appel à Alfresco à échoué :\n- Code: %s\n\n- Url\n%s\n\n- Détail\n%s"
                )
                % (e.status, e.url, e.details)
            )
        except Exception as e:
            raise exceptions.ValidationError(e)

    def push_alfresco_file_api(self, file, name, metadata=None, folder=None):
        """
        Ajoute un fichier sur la GED Alfresco
        @return: fonction get_partner_files() de res.partner
        """
        if not folder:
            raise exceptions.ValidationError(
                _("Le dossier parent n'existe pas ou n'est pas renseigné !")
            )

        # url = "/nodes/%s/children" % folder
        url = (
            "/alfresco/api/-default-/public/alfresco/versions/1/nodes/%s/children?majorVersion=true"
            % folder
        )
        _logger.info("Chargement document Alfresco : %s" % name)

        content_type = "multipart/form-data"

        params = {
            "name": name,
            "nodeType": "cmis:document",
            "overwrite": True,
            "properties": metadata,
        }
        files = {"filedata": (name, file, content_type, params)}
        return self.alfresco_get_by_url(url=url, call_type="post", files=files).json()