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

import csv
import io
import datetime
import logging
from ftplib import FTP

from odoo import models, fields, exceptions

_logger = logging.getLogger(__name__)


class CgscopRigaOdooImport(models.Model):
    """ Ce modèle permet de logguer les imports de RIGA
    vers Odoo
    """
    _name = 'riga.odoo.import'
    _description = 'Synchronisation RIGA vers Odoo'
    _rec_name = 'model_id'
    _order = 'create_date desc'

    model_id = fields.Many2one(
        comodel_name='ir.model',
        string='Modèle')
    is_sync = fields.Boolean('Synchronisé', default=False)
    is_warning = fields.Boolean('Warning', default=False)
    log = fields.Text('Log')
    is_error = fields.Boolean('Erreur Synchro', default=False)

    # ------------------------------------------------------
    # Fonctions génériques
    # ------------------------------------------------------
    def _get_ftp_file(self, filename, ftp_channel):
        """ Fonction de récupértion d'ufichier sur un FTP.

        Cette fonction prend en paramètre un nom de fichier et recherche
        dans le fichier de conf Odoo les login et password, puis dans les
        paramètres système l'url et le path pour se connecter et retourner
        le fichier binaire souhaité.

        : param filename: (string) nom du fichier

        @return csv_file: (binary) fichier binaire à traiter
        """
        param = ftp_channel
        ftp_login = param.login
        ftp_pass = param.password
        # Connexion FTP
        try:
            # Connexion ftp
            ftp = FTP(param.url, ftp_login, ftp_pass)
            # Changement de path
            ftp.cwd(param.path)
            # Lecture du fichier
            csv_file = io.BytesIO()
            ftp.retrbinary(
                "RETR " + filename,
                csv_file.write)
            _logger.info(
                "Connecté à l'URL : %s  - Path : %s",
                param.url,
                param.path
            )
            return csv_file
        except Exception as e:
            raise exceptions.Warning(e)

    def _read_file(self, model, file, table, primary_key, header_key):
        """ Cette fonction prend en paramètre un fichier CSV et lit
        chaque ligne pour mettre à jour la ligne de la table correspondante
        si elle existe ou la créer si l'organisme existe

        :param model (obj): modèle pour importer les données
        :param file (binary): fichier CSV à traiter
        :param table (list): liste de tuples de correspondance
                            ('champ_odoo', 'champ_csv')
        :param primary_key (string): header du fichier CSV correspondant à la clé
        :param parent_id (string):  header du fichier CSV pour lequel on va
                                    rechercher l'ID RIGA de l'organisme
        """
        # try:
        file.seek(0)
        # Création du lecteur CSV.
        reader = csv.DictReader(
            io.TextIOWrapper(file, encoding="utf-8-sig", newline=None),
            delimiter=';')
        model_obj = self.env[model.model]
        crea_nb = 0
        maj_nb = 0
        log = ""
        # Lecture de chaque ligne
        for row in reader:
            # Création de l'objet
            vals = {}
            for field in table:
                print
                vals.update({
                    field[0]: self._cast_type(
                        model=model,
                        field=field[0],
                        value=row[field[1]],
                        relation=field[2])
                })
            # Vérification de la ligne dans la base
            line = model_obj.search([
                ['id_riga', '=', row[primary_key]]])
            # Si il y a un enregistrement, on met à jour
            if line:
                line.write(vals)
                maj_nb += 1
            else:
                # Check d'un organisme dans la base
                partner = self.env['res.partner'].search([
                    ['id_riga', '=', row[header_key]],
                    ['is_cooperative', '=', True]])
                # Si l'organisme existe, on crée un enregistrement
                if partner:
                    vals.update({
                        'partner_id': partner.id
                    })
                    model_obj.create(vals)
                    crea_nb += 1
                else:
                    log += ("Pas d'enredistrement trouvé pour l'id " +
                            row[primary_key] + " avec l'id riga " +
                            row[header_key] + "\n")

        # Création du log
        self.create({
            'model_id': model.id,
            'is_sync': True,
            'log': ("Import du fichier réussi : \n" + 
                    " - Création de " + str(crea_nb) + " lignes\n" +
                    " - Mise à jour de " + str(maj_nb) + " lignes\n\n" + log),
            'is_warning': True if log else False,
        })

        # except Exception as e:
        #     _logger.error(e.__str__())
        #     # Création du log
        #     self.create({
        #         'model_id': model.id,
        #         'is_sync': False,
        #         'is_error': True,
        #         'log': str(e)
        #     })

    def _cast_type(self, model, field, value, relation=None):
        """ Détermine en fonction du champ la valeur typée à renvoyer
        pour l'ORM

        :param model : objet modèle de donnée Odoo
        :param field : nom du champ
        :param value : valeur à caster

        @return valeur castée
        """
        odoo_field = self.env['ir.model.fields'].search([
            ['model', '=', model.model],
            ['name', '=', field]])
        field_type = odoo_field.ttype
        if value:
            if field_type == 'integer':
                return int(float(value.replace(',', '.')))
            elif field_type == 'float':
                return float(value.replace(',', '.'))
            elif field_type == 'date':
                date_value = datetime.datetime.strptime(
                    value, '%d/%m/%Y')
                return date_value
            elif field_type == 'datetime':
                date_value = datetime.datetime.strptime(
                    value, '%d/%m/%Y %H:%M:%S')
                return date_value
            elif field_type == 'selection':
                selection = self.env[model.model]._fields.get(odoo_field.name).selection
                for item in selection:
                    if item[1] == value:
                        return item[0]
            elif field_type == 'many2one':
                m2o_value = self.env[odoo_field.relation].search([
                    [relation, '=', value]]).id
                return m2o_value
            else:
                return value
        else:
            return False

    # ------------------------------------------------------
    # Import données
    # ------------------------------------------------------

    def odoo_sync(self):
        model_sync = self.env['riga.files.matching'].sudo().search([
            ['is_active', '=', True]])
        self.sync_file(model_sync)

    def sync_file(self, model_sync):
        """ Fonction de synchronisation des fichiers CSV RIGA
        avec les tables associées Odoo.

        La fonction recherche l'ensembles des synchronisations actives
        dans la table riga.files.matching, elle transforme la table
        de correspondance de string à list puis appelle la fonction
        _read_file qui permet d'intégrer ou de metre à jour les valeurs
        """
        for model in model_sync:
            table = []
            # Calcul nom du fichier
            if model.params_active:
                date_value = fields.Datetime.now() + datetime.timedelta(days=model.day)
                filename = (model.riga_filename +
                            date_value.strftime(model.suffix) + '.' +
                            model.riga_extension)
            else:
                filename = (model.riga_filename + '.' +
                            model.riga_extension)
            _logger.info(
                "Lecture Fichier %s pour le modèle %s",
                filename,
                model.model_id.name
            )
            for header in model.matching_table_ids:
                table.append([header.name, header.riga_name, header.relation])
            self._read_file(
                model=model.model_id,
                file=self._get_ftp_file(model.riga_filename, model.ftp_channel_id),
                table=table,
                primary_key=model.primary_key,
                header_key=model.header_key)