<?php
namespace App\Controller;
/*
 * kohinos_cooperatic
 * Copyright (C) 2019-2020  ADML63
 * Copyright (C) 2020- Cooperatic
 *
 * 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/>.
 */
 

use App\Entity\Adherent;
use App\Entity\ContactComptoir;
use App\Entity\ContactPrestataire;
use App\Entity\Comptoir;
use App\Entity\CotisationAdherent;
use App\Entity\CotisationPrestataire;
use App\Entity\EtatPrestataire;
use App\Entity\Geoloc;
use App\Entity\GeolocPrestataire;
use App\Entity\Groupe;
use App\Entity\Import;
use App\Entity\Prestataire;
use App\Entity\Rubrique;
use App\Entity\Siege;
use App\Entity\User;
use App\Entity\Usergroup;
use App\Entity\Flux;
use App\Entity\TypePrestataire;
use App\Enum\ImportEnum;
use App\Enum\MoyenEnum;
use App\Events\MLCEvents;
use App\Form\Type\ImportFormType;
use Gedmo\Sluggable\Util as Sluggable;
use Behat\Transliterator\Transliterator;
use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityManagerInterface;
use FOS\UserBundle\Event\UserEvent;
use FOS\UserBundle\Model\UserInterface;
use FOS\UserBundle\Model\UserManagerInterface;
use FOS\UserBundle\Util\TokenGeneratorInterface;
use FOS\UserBundle\Util\UserManipulator;
use Sonata\AdminBundle\Controller\CRUDController;
use Symfony\Component\Finder\Finder;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

class ImportController extends CRUDController
{
    protected $siege;
    protected $header;
    protected $warnings;
    protected $errors;
    protected $nberrors;
    protected $nbsuccess;
    protected $success;
    protected $lineErrors;
    protected $em;
    protected $file;
    protected $security;
    protected $userManager;
    protected $translator;
    protected $tokenGenerator;
    protected $eventDispatcher;

    public function __construct(EntityManagerInterface $em,
                                Security $security,
                                UserManagerInterface $userManager,
                                TranslatorInterface $translator,
                                TokenGeneratorInterface $tokenGenerator,
                                EventDispatcherInterface $eventDispatcher)
    {
        $this->header = null;
        $this->warnings = array();
        $this->errors = array();
        $this->nberrors = 0;
        $this->nbsuccess = 0;
        $this->lineErrors = array();
        $this->em = $em;
        $this->security = $security;
        $this->userManager = $userManager;
        $this->translator = $translator;
        $this->tokenGenerator = $tokenGenerator;
        $this->eventDispatcher = $eventDispatcher;
        $this->siege = null;
        $this->sendemail = false;
    }

    public function createAction()
    {
        $this->siege = $this->em->getRepository(Siege::class)->findOneById(1);
        $import = new Import();
        $import->setUser($this->getUser());
        $form = $this->createForm(ImportFormType::class, $import);
        $form->handleRequest($this->getRequest());

        if ($form->isSubmitted() && $form->isValid()) {
            $import = $form->getData();
            $media = $import->getMedia();
            $type = $import->getType();
            $this->sendemail = (bool)$import->getSendemail();

            // Sauvegarder l'import en base de données avant l'essai d'import
            $this->em->persist($import);
            $this->em->flush();
            $idimport = $import->getId();

            try {
                $this->importFromCSV($type, $media);

                $import = $this->em->getRepository(Import::class)->findOneById($idimport);
                $import->setEnabled(true);

                $import->setSuccess(json_encode($this->success));
                $import->setWarnings(json_encode($this->warnings));
                $import->setErrors(json_encode($this->errors));
                $import->setNbentityadded($this->nbsuccess);
                $import->setNbentityerror($this->nberrors);
                $this->em->persist($import);
                $this->em->flush();

                if (empty($this->errors)) {
                    $this->addFlash(
                        'success',
                        'Import effectué avec succès !'
                    );
                } else {
                    $this->addFlash(
                        'error',
                        "Il y a eu des erreurs lors de l'import !"
                    );
                }
                return $this->redirect($this->admin->generateUrl('show', array('id' => $import->getId())));
            } catch (\Exception $e) {

                if ('prod' === $this->container->get('kernel')->getEnvironment()) {
                    return $this->renderWithExtraParams('admin/import_error.html.twig', [
                        'action' => 'list',
                        'error' => $e->getMessage(),
                    ]);
                }

                throw $e;
            }
        }

        return $this->renderWithExtraParams('admin/import.html.twig', array(
            'action' => 'list',
            'form' => $form->createView(),
            'errors' => $this->errors,
            'warnings' => $this->warnings,
            'success' => $this->success,
            'linkcsverror' => (count($this->lineErrors) > 0)?$this->generateUrl('getcsv', array('header' => $this->header, 'data' => array_values($this->lineErrors))):null,
            'csvparams' => $this->getParameter('app.import.header')
        ));
    }

    private function importFromCSV($type, $media)
    {
        // Turning off doctrine default logs queries for saving memory
        $this->em->getConnection()->getConfiguration()->setSQLLogger(null);

        // Get file provider
        $provider = $this->container->get($media->getProviderName());

        $csvRows = $this->parseCSV($provider->getFilesystem()->getAdapter()->getDirectory(), $provider->getReferenceImage($media));
        $this->header = implode(';', array_values($csvRows[0]));

        $config = $this->getParameter('app.import.header');

        if ($type == ImportEnum::IMPORT_ADHERENT) {
            $result = $this->importAdherent($csvRows);
        } elseif ($type == ImportEnum::IMPORT_PRESTATAIRE) {
            $result = $this->importPrestataire($csvRows);
        } elseif ($type == ImportEnum::IMPORT_GROUPE) {
            $result = $this->importGroupe($csvRows);
        } elseif ($type == ImportEnum::IMPORT_COMPTOIR) {
            $result = $this->importComptoir($csvRows);
        } else {
            // Ne devrait jamais arriver, mais sait-on jamais !
            $this->errors['error'] = $this->translator->trans('Choisir un type de données à importer !');
        }

        return $result;
    }

    private function slugify($string)
    {
        $string = Transliterator::transliterate($string, '-');
        return Transliterator::urlize($string, '-');
    }

    private function importComptoir($csvRows)
    {
        // Iterate over the reader and write each row to the database
        //groupe;nom;description;adresse;cpostal;ville;latitude;longitude;compte;gestionnaire_email1;gestionnaire_nom1;gestionnaire_prenom1;gestionnaire_phone1;gestionnaire_mobile1;contact1;phone1;email1;contact2;phone2;email2
        $line = 1;
        foreach ($csvRows as $row) {
            if ($line == 1) {
                $line++;
                continue;
            }
            if (!(array_key_exists("groupe", $row) && array_key_exists("nom", $row))) {
                $this->addError($row, $line, 'nom & groupe', $this->translator->trans("Les colonnes 'nom' et 'groupe' sont obligatoires !"));
                $this->nberrors++;
                $line++;
                continue;
            }

            $groupe = array_key_exists("groupe", $row) ? $row['groupe'] : '';
            $nom = array_key_exists("nom", $row) ? $row['nom'] : '';
            if (empty($groupe)) {
                $this->addError($row, $line, 'groupe', $this->translator->trans("Le 'groupe' est obligatoire !"));
                $this->nberrors++;
                $line++;
                continue;
            }
            if (empty($nom)) {
                $this->addError($row, $line, 'nom', $this->translator->trans("Le 'nom' est obligatoire !"));
                $this->nberrors++;
                $line++;
                continue;
            }
            $description = array_key_exists("description", $row) ? $row['description'] : '';
            $adresse = array_key_exists("adresse", $row) ? $row['adresse'] : '';
            $cpostal = array_key_exists("cpostal", $row) ? $row['cpostal'] : '';
            $ville = array_key_exists("ville", $row) ? $row['ville'] : '';
            $latitude = array_key_exists("latitude", $row) ? $row['latitude'] : '';
            $longitude = array_key_exists("longitude", $row) ? $row['longitude'] : '';
            $compte = array_key_exists("compte", $row) ? $row['compte'] : '';

            $groupeFound = $this->em->getRepository(Groupe::class)->findOneBy(array('slug' => $this->slugify($groupe)));
            if (empty($groupeFound)) {
                $groupeFound = new Groupe();
                $groupeFound->setName($groupe);
                $groupeFound->setSiege($this->siege);
                $this->em->persist($groupeFound);
                $this->addSuccess($row, $line, 'groupe', $this->translator->trans('Groupe ajouté : ').$groupe);
            }
            $comptoir = $this->em->getRepository(Comptoir::class)->findOneBy(array('slug' => $this->slugify($nom), 'groupe' => $groupeFound));
            if (empty($comptoir)) {
                $comptoir = new Comptoir();
                $comptoir->setGroupe($groupeFound);
                $comptoir->setName($nom);
                if (!empty($description)) {
                    $comptoir->setContent($description);
                } else {
                    $this->addWarning($row, $line, 'description', 'empty');
                }
                if (!empty($compte)) {
                    $comptoir->setCompte($this->tofloat($compte));
                } else {
                    $this->addWarning($row, $line, 'compte', 'empty');
                }
                if (!empty($adresse) || !empty($cpostal) || !empty($ville)) {
                    $geolocFound = new Geoloc();
                    $geolocFound->setAdresse($adresse);
                    $geolocFound->setCpostal(intval($cpostal));
                    $geolocFound->setVille($ville);
                    if (!empty($latitude) && !empty($longitude)) {
                        $geolocFound->setLat((float)$latitude);
                        $geolocFound->setLon((float)$longitude);
                    }
                    $comptoir->setGeoloc($geolocFound);
                }
                // Importer les contacts du comptoir s'ils existent (par défaut en public)
                $cptContact = 1;
                while (array_key_exists("contact".$cptContact, $row) && $cptContact < 10) {
                    $contact = array_key_exists('contact'.$cptContact, $row) ? $row['contact'.$cptContact] : '';
                    $phone = array_key_exists('phone'.$cptContact, $row) ? $row['phone'.$cptContact] : '';
                    $email = array_key_exists('email'.$cptContact, $row) ? $row['email'.$cptContact] : '';
                    $contactC = new ContactComptoir();
                    $contactC->setComptoir($comptoir);
                    $contactC->setEnabled(true);
                    $contactC->setName($contact);
                    $contactC->setTel($phone);
                    $contactC->setEmail($email);
                    $this->em->persist($contactC);
                    ++$cptContact;
                }

                // Importer les gestionnaires de comptoir s'ils existent
                $groupeGestionnaire = $this->em->getRepository(Usergroup::class)->findOneByName('Comptoir');
                $gestionnaires = $this->importGestionnaires($row, $groupeGestionnaire);
                $comptoir->setGestionnaires($gestionnaires);

                $this->addSuccess($row, $line, 'comptoir', $this->translator->trans('Comptoir ajouté : ').$nom);
                $this->nbsuccess++;
                $this->em->persist($comptoir);
                $this->em->flush();
                $this->em->clear();
            } else {
                $this->addError($row, $line, 'nom', $this->translator->trans("Le comptoir avec ce nom {name} existe déjà !", ['name' => $nom]));
                $this->nberrors++;
            }

            $line++;
        }
        ksort($this->errors);
        ksort($this->warnings);
    }

    private function importGroupe($csvRows)
    {
        // Iterate over the reader and write each row to the database
        // nom;description;compte;gestionnaire_email1;gestionnaire_nom1;gestionnaire_prenom1;gestionnaire_phone1;gestionnaire_mobile1
        $line = 1;
        foreach ($csvRows as $row) {
            if ($line == 1) {
                $line++;
                continue;
            }
            $name = array_key_exists("nom", $row) ? $row['nom'] : '';
            if (empty($name)) {
                $this->addError($row, $line, 'nom', $this->translator->trans("Le 'nom' est obligatoire !"));
                $this->nberrors++;
                $line++;
                continue;
            }
            $description = array_key_exists("description", $row) ? $row['description'] : '';
            $compte = array_key_exists("compte", $row) ? $row['compte'] : '';
            $groupe = $this->em->getRepository(Groupe::class)->findOneBy(array('slug' => $this->slugify($name)));
            if (empty($groupe)) {
                $groupe = new Groupe();
                $groupe->setSiege($this->em->getRepository(Siege::class)->findOneById(1));
                if (!empty($name)) {
                    $groupe->setName($name);
                } else {
                    $this->addWarning($row, $line, 'name', 'empty');
                }
                if (!empty($content)) {
                    $groupe->setContent($content);
                } else {
                    $this->addWarning($row, $line, 'content', 'empty');
                }
                if (!empty($compte)) {
                    $groupe->setCompte($this->tofloat($compte));
                } else {
                    $this->addWarning($row, $line, 'compte', 'empty');
                }
                // Importer les gestionnaires du groupe s'ils existent
                $groupeGestionnaire = $this->em->getRepository(Usergroup::class)->findOneByName('Gestionnaire de Groupe');
                $gestionnaires = $this->importGestionnaires($row, $groupeGestionnaire);
                $groupe->setGestionnaires($gestionnaires);

                $this->addSuccess($row, $line, 'groupe', $this->translator->trans('Groupe ajouté : ').$name);
                $this->em->persist($groupe);
                $this->nbsuccess++;
                $this->em->flush();
                $this->em->clear();
            } else {
                $this->addError($row, $line, 'name', $this->translator->trans("Le groupe avec ce nom '".$name."' existe déjà !"));
                $this->nberrors++;
            }
            $line++;
        }
        ksort($this->errors);
        ksort($this->warnings);
    }

    private function importPrestataire($csvRows)
    {
        $operateur = $this->getUser();
        $operateur_id = $operateur->getId();
        $line = 0;

        /**
         * Iterate over the reader and write each row to the database.
         * Each row has at least 6 required columns.
         *
         * Row format: groupe;adresse;cpostal;ville;raison;ecompte;metier;statut;responsable;iban;siret;web;horaires;...
         *             description;rubriques;tags;tauxreconversion;cotisations;...
         *             gestionnaire_email1;gestionnaire_nom1;gestionnaire_prenom1;gestionnaire_phone1;gestionnaire_mobile1;...
         *             contact1;phone1;email1;contact2;phone2;email2
         */
        $requiredColumns = [
            'groupe', 'raison', 'siret', 'adresse', 'cpostal', 'ville',
        ];

        $warningColumns = [
            'metier', 'statut', 'responsable', 'iban', 'web', 'horaires', 'description', 'ecompte',
        ];

        $columns = array_fill_keys(
            array_merge($requiredColumns, $warningColumns, ['rubriques', 'tags', 'tauxreconversion', 'cotisations']),
            null
        );

        foreach ($csvRows as $row) {
            $hasError = false;
            $line++;

            // The header is ignored
            if ($line === 1) {
                continue;
            }

            // Add the missing columns to the current row (with a null value).
            $row += $columns;

            /**
             * Check the required columns.
             * If one is not present or empty => add an error and go to the next line.
             */
            foreach ($requiredColumns as $column) {
                if (! $row[$column]) {
                    $this->addError($row, $line, $column,
                        $this->translator->trans('prestataire.column_required', [$column], 'import')
                    );
                    $hasError = true;
                }
            }

            if ($hasError) {
                $this->nberrors++;
                continue;
            }

            /**
             * If it doesn't exist yet, create and fill a Prestataire.
             * Otherwise => error!
             */
            $prestataire = $this->em->getRepository(Prestataire::class)->findOneBy([
                'slug' => $this->slugify($row['raison'])
            ]);

            if (!$prestataire) {
                $prestataire = new Prestataire();

                // Actually, each new Prestataire will have the 'prestataire' as TypePrestataire
                // @TODO: add a column 'type' in the CSV file (to choose a TypePrestataire)?
                $prestataire->setTypePrestataire($this->em->getRepository(TypePrestataire::class)->findOneBy([
                    'slug' => 'prestataire'
                ]));

                foreach ($requiredColumns as $column) {
                    // Special processings for these fields => so we skip them
                    if (in_array($column, ['groupe', 'adresse', 'cpostal', 'ville'])) continue;

                    $prestataire->{'set'.ucfirst($column)}($row[$column]);
                }

                // Special processing for the 'ecompte' field
                if ($e =& $row['ecompte']) {
                    $e = $this->tofloat($e);
                }

                foreach ($warningColumns as $column) {
                    if ($row[$column]) {
                        $prestataire->{'set'.ucfirst($column)}($row[$column]);
                    } else {
                        $this->addWarning($row, $line, $column, 'empty');
                    }
                }

                /**
                 * If one or many Contacts are present, we create them for the Prestataire.
                 * If a phone or an email is defined, the associated 'contact' field must be defined too!
                 */
                for ($i = 1; $i < 10; $i++) {
                    $name  = $row["contact$i"] ?? null;
                    $phone = $row["phone$i"]   ?? null;
                    $email = $row["email$i"]   ?? null;

                    if ($name) {
                        $contact = new ContactPrestataire();
                        $contact->setPrestataire($prestataire);
                        $contact->setEnabled(true);
                        $contact->setName($name);
                        $contact->setTel($row["phone$i"] ?? '');
                        $contact->setEmail($row["email$i"] ?? '');

                        $this->em->persist($contact);
                    } elseif ($phone || $email) {
                        $this->addError($row, $line, "contact$i",
                            $this->translator->trans('prestataire.column_required', ["contact$i"], 'import')
                        );
                        $hasError = true;
                    }
                }

                if ($hasError) {
                    $this->nberrors++;
                    continue;
                }

                // Importer les gestionnaires du prestataire s'ils existent
                $groupeGestionnaire = $this->em->getRepository(Usergroup::class)->findOneByName('Prestataire');
                $gestionnaires = $this->importGestionnaires($row, $groupeGestionnaire);
                $prestataire->setUsers($gestionnaires);

                /**
                 * If no Groupe object exists for the 'groupe' field, we create one.
                 * Note: at this point, the 'groupe' field is necessarily not empty (because it was required above).
                 */
                $groupe = $row['groupe'];
                $groupeObj = $this->em->getRepository(Groupe::class)->findOneBy(['name' => $groupe]);

                if (! $groupeObj) {
                    $groupeObj = new Groupe();
                    $groupeObj->setName($groupe);
                    $groupeObj->setSiege($this->siege);
                    $this->em->persist($groupeObj);
                    $this->addSuccess($row, $line, 'groupe',
                        $this->translator->trans('prestataire.groupe_notfound_created', [$groupe], 'import')
                    );
                }
                $prestataire->setGroupe($groupeObj);

                if ($cotisations = $row['cotisations']) {
                    $cotisationArray = explode(',', $cotisations);
                    foreach ($cotisationArray as $cotisationDetails) {
                        $cotisation = new CotisationPrestataire();
                        $now = new DateTime();
                        $cotisation->setReference('Import du '.$now->format('d/m/Y H:i'));
                        $cotisation->setOperateur($operateur);
                        $cotisation->setRole('ROLE_SUPER_ADMIN');
                        $cotisation->setExpediteur($prestataire);
                        $cotisation->setMoyen(MoyenEnum::MOYEN_AUTRE);
                        $cotisationDetailsArray = explode(':', $cotisationDetails);
                        if (count($cotisationDetailsArray) == 1) {
                            $cotisation->setMontant(intval($cotisationDetails[0]));
                            $cotisation->getCotisationInfos()->setDebut($now);
                            $cotisation->getCotisationInfos()->setFin(new DateTime('+ 1 year'));
                        } else {
                            $cotisation->setMontant(intval($cotisationDetailsArray[0]));
                            $cotisation->getCotisationInfos()->setAnnee(intval($cotisationDetailsArray[1]));
                            $cotisation->getCotisationInfos()->setDebut(DateTime::createFromFormat('Ymd', intval($cotisationDetailsArray[1]).'0101'));
                            $cotisation->getCotisationInfos()->setFin(DateTime::createFromFormat('Ymd', intval($cotisationDetailsArray[1]).'1231'));
                        }
                        $this->em->persist($cotisation);
                        $this->addSuccess($row, $line, 'cotisations', $this->translator->trans('Cotisation ajouté : ').$groupe);
                    }
                } else {
                    $this->addWarning($row, $line, 'cotisations', 'empty');
                }

                if ($rubriques = $row['rubriques']) {
                    $rubriquesArray = explode(',', $rubriques);
                    foreach ($rubriquesArray as $rubrique) {
                        $rubriquesFound = $this->em->getRepository(Rubrique::class)->findOneBy(array('slug' => $this->slugify($rubrique)));
                        if (empty($rubriquesFound)) {
                            $rubriquesFound = new Rubrique();
                            $rubriquesFound->setName($rubrique);
                            $this->em->persist($rubriquesFound);
                            $this->addSuccess($row, $line, 'rubrique', $this->translator->trans('Rubrique ajoutée : ').$rubrique);
                        }
                        $prestataire->addRubrique($rubriquesFound);
                    }
                } else {
                    $this->addWarning($row, $line, 'rubriques', 'empty');
                }

                if ($tags = $row['tags']) {
                    $tagsArray = explode(',', $tags);
                    foreach ($tagsArray as $tag) {
                        $tagsFound = $this->em->getRepository(EtatPrestataire::class)->findOneBy(array('slug' => $this->slugify($tag)));
                        if (empty($tagsFound)) {
                            $tagNew = new EtatPrestataire();
                            $tagNew->setName($tag);
                            $this->em->persist($tagNew);
                            $this->addSuccess($row, $line, 'tag', $this->translator->trans('Tag ajouté : ').$tag);
                        }
                    }
                } else {
                    $this->addWarning($row, $line, 'tags', 'empty');
                }

                /**
                 * Add a Geoloc for this Prestataire.
                 * At this point, 'adresse', 'cpostal' and 'ville' fields exist and are not empty.
                 */
                $geoloc = new Geoloc();
                $geoloc->setEnabled(true);
                $geoloc->setAdresse($row['adresse']);
                $geoloc->setCpostal($row['cpostal']);
                $geoloc->setVille($row['ville']);

                $geolocP = new GeolocPrestataire();
                $geolocP->setName('Adresse');
                $geolocP->setPrestataire($prestataire);
                $geolocP->setGeoloc($geoloc);
                $this->em->persist($geolocP);

                $prestataire->setGeolocs([$geolocP]);

            } else {
                $this->addError($row, $line, 'raison',
                    $this->translator->trans('prestataire.already_exists', [$row['raison']], 'import')
                );
                $this->nberrors++;
                $hasError = true;
            }

            if (!$hasError) {
                $this->em->persist($prestataire);
                $this->em->flush();
                $this->em->clear();

                // em is fully cleared: we need to refetch entities we get from em during process
                $operateur = $this->em->getRepository(User::class)->findOneById($operateur_id);
                $this->siege = $this->em->getRepository(Siege::class)->findOneById(1);

                $this->addSuccess($row, $line, 'user',
                    $this->translator->trans('prestataire.added', [(string) $prestataire], 'import')
                );
                $this->nbsuccess++;
            }
        }

        ksort($this->errors);
        ksort($this->warnings);
    }

    private function importAdherent($csvRows)
    {
        // Batch operations with doctrine require some workaround
        $operateur = $this->getUser();
        $operateur_id = $operateur->getId();
        // Iterate over the reader and write each row to the database
        // groupe;firstname;lastname;email;phone;mobile;adresse;cpostal;ville;ecompte
        $line = 1;
        foreach ($csvRows as $row) {
            $hasError = false;
            if ($line == 1) {
                $line++;
                continue;
            }
            $groupe = array_key_exists("groupe", $row) ? $row['groupe'] : '';
            $firstname = array_key_exists("firstname", $row) ? $row['firstname'] : '';
            $lastname = array_key_exists("lastname", $row) ? $row['lastname'] : '';
            $email = array_key_exists("email", $row) ? $row['email'] : '';
            $phone = array_key_exists("phone", $row) ? $row['phone'] : '';
            $mobile = array_key_exists("mobile", $row) ? $row['mobile'] : '';
            $adresse = array_key_exists("adresse", $row) ? $row['adresse'] : '';
            $cpostal = array_key_exists("cpostal", $row) ? $row['cpostal'] : '';
            $ville = array_key_exists("ville", $row) ? $row['ville'] : '';
            $ecompte = array_key_exists("ecompte", $row) ? $row['ecompte'] : '';
            $cotisations = array_key_exists("cotisations", $row) ? $row['cotisations'] : '';

            if (!(array_key_exists("email", $row))) {
                $this->addError($row, $line, 'email', $this->translator->trans("La colonne 'email' est obligatoire !"));
                $line++;
                $this->nberrors++;
                continue;
            }

            $adherent = new Adherent();
            $user = $this->userManager->createUser();
            $user->setConfirmationToken($this->tokenGenerator->generateToken());
            $user->setEnabled(true);
            $user->setPassword(md5(random_bytes(10)));
            $usergroupe = $this->em->getRepository(Usergroup::class)->findOneByName('Adherent');
            $user->addPossiblegroup($usergroupe);
            $user->setGroups([$usergroupe]);
            $user->setAdherent($adherent);
            $adherent->setUser($user);

            if (!empty($firstname)) {
                $user->setFirstname($firstname);
            } else {
                $this->addWarning($row, $line, 'firstname', 'empty');
            }
            if (!empty($lastname)) {
                $user->setLastname($lastname);
            } else {
                $this->addWarning($row, $line, 'lastname', 'empty');
            }
            if (!empty($email)) {
                $userFound = $this->em->getRepository(User::class)->findOneBy(array('email' => $email));
                if (!empty($userFound)) {
                    $hasError = true;
                    $this->addError($row, $line, 'email', $this->translator->trans("L'email est déjà utilisé !"));
                    $line++;
                    $this->nberrors++;
                    continue;
                } else {
                    $user->setEmail($email);
                    $user->setUsername($email);
                }
            } else {
                $this->addWarning($row, $line, 'email', 'empty');
            }
            if (!empty($phone)) {
                $user->setPhone($phone);
            } else {
                $this->addWarning($row, $line, 'phone', 'empty');
            }
            if (!empty($mobile)) {
                $user->setMobile($mobile);
            } else {
                $this->addWarning($row, $line, 'mobile', 'empty');
            }
            if (!empty($ecompte)) {
                $adherent->setEcompte($this->tofloat($ecompte));
            } else {
                $this->addWarning($row, $line, 'ecompte', 'empty');
            }
            if (!empty($groupe)) {
                $groupeFound = $this->em->getRepository(Groupe::class)->findOneBy(array('name' => $groupe));
                if (empty($groupeFound)) {
                    $groupeFound = new Groupe();
                    $groupeFound->setName($groupe);
                    $groupeFound->setSiege($this->siege);
                    $this->em->persist($groupeFound);
                    $this->addSuccess($row, $line, 'groupe', $this->translator->trans('Groupe ajouté : ').$groupe);
                }
                $adherent->setGroupe($groupeFound);
            } else {
                $this->addWarning($row, $line, 'groupe', 'empty');
            }
            if (!empty($cotisations)) {
                $cotisationArray = explode(',', $cotisations);
                if (count($cotisationArray) > 0) {
                    foreach ($cotisationArray as $cotisationDetails) {
                        $cotisation = new CotisationAdherent();
                        $now = new DateTime();
                        $cotisation->setReference('Import du '.$now->format('d/m/Y H:i'));
                        $cotisation->setOperateur($operateur);
                        $cotisation->setRole('ROLE_ADHERENT');
                        $cotisation->setExpediteur($adherent);
                        $cotisation->setMoyen(MoyenEnum::MOYEN_AUTRE);
                        $cotisationDetailsArray = explode(':', $cotisationDetails);
                        if (count($cotisationDetailsArray) == 1) {
                            // fix #201: A 'cotisation' can't be zero! (because of the Flux error management)
                            if (0 === intval($cotisationDetails)) {
                                $this->addError($row, $line, 'cotisations',
                                    $this->translator->trans('adherent.cotisations.isZero', [], 'import')
                                );
                                $this->nberrors++;
                                $line++;
                                continue;
                            }

                            $cotisation->setMontant(intval($cotisationDetails));
                            $cotisation->getCotisationInfos()->setDebut($now);
                            $cotisation->getCotisationInfos()->setFin(new DateTime('+ 1 year'));
                        } else {
                            // fix #201: A 'cotisation' can't be zero! (because of the Flux error management)
                            if (0 === intval($cotisationDetailsArray[0])) {
                                $this->addError($row, $line, 'cotisations',
                                    $this->translator->trans('adherent.cotisations.isZero', [], 'import')
                                );
                                $this->nberrors++;
                                $line++;
                                continue;
                            }

                            $cotisation->setMontant(intval($cotisationDetailsArray[0]));
                            $cotisation->getCotisationInfos()->setAnnee(intval($cotisationDetailsArray[1]));
                            $cotisation->getCotisationInfos()->setDebut(DateTime::createFromFormat('Ymd', intval($cotisationDetailsArray[1]).'0101'));
                            $cotisation->getCotisationInfos()->setFin(DateTime::createFromFormat('Ymd', intval($cotisationDetailsArray[1]).'1231'));
                        }
                        $this->em->persist($cotisation);
                        $this->addSuccess($row, $line, 'cotisations', $this->translator->trans('Cotisation ajoutée : ').$groupe);
                    }
                }
            } else {
                $this->addWarning($row, $line, 'cotisations', 'empty');
            }
            if (!empty($adresse) || !empty($cpostal) || !empty($ville)) {
                $geolocFound = new Geoloc();
                $geolocFound->setAdresse($adresse);
                $geolocFound->setCpostal(intval($cpostal));
                $geolocFound->setVille($ville);
                $adherent->setGeoloc($geolocFound);
            }
            if (!$hasError) {
                $this->addSuccess($row, $line, 'user', $this->translator->trans('Utilisateur bien ajouté : ').$user->__toString());
                $this->nbsuccess++;
                $user->setPasswordRequestedAt(new \DateTime());
                $this->userManager->updateUser($user);
                if ($this->sendemail) {
                    $this->eventDispatcher->dispatch(MLCEvents::REGISTRATION_ADHERENT, new UserEvent($user, $this->getRequest()));
                }
                $this->em->flush();
                $this->em->clear();
                // em is fully cleared: we need to refetch entities we get from em during process
                $operateur = $this->em->getRepository(User::class)->findOneBy(array('id' => $operateur_id));
            }

            $line++;
        }
        ksort($this->errors);
        ksort($this->warnings);
    }

    /**
     * Open and parse .CSV file
     * @param  string  $filePath        Path of the file
     * @param  string  $fileName        Name of the file
     * @param  boolean $ignoreFirstLine If true, ignore first line
     * @return array  Array of parsed values with array's key = firstline
     */
    private function parseCSV($filePath, $fileName, $ignoreFirstLine = false)
    {
        $csv = new \SplFileObject($filePath.'/'.$fileName);

        $rows = array();
        $firstline = null;
        if (($handle = fopen($csv->getRealPath(), "r")) !== false) {
            $i = 0;
            while (($data = fgetcsv($handle, null, ";")) !== false) {
                $i++;
                if ($i == 1) {
                    $firstline = $data;
                    $rows[] = $data;
                    if ($ignoreFirstLine) {
                        continue;
                    }
                } else {
                    if (count($firstline) != count($data)) {
                        $this->addError($data, $i, 'Ligne entière', $this->translator->trans("La ligne ne contient pas le bon nombre d'éléments requis !"));
                        $this->nberrors++;
                        continue;
                    }
                    $rows[] = array_combine(array_values($firstline), array_values($data));
                }
            }
            fclose($handle);
        }
        return $rows;
    }

    /**
     * Import manager of comptoir / groupe / presta
     * @param  array    $row    Value of line imported
     * @param  Usergroup $groupe Groupe add to manager imported
     * @return User  Manager created
     */
    private function importGestionnaires($row, Usergroup $groupe)
    {
        $cptGestionnaire = 1;
        $users = new ArrayCollection();
        while (array_key_exists("gestionnaire_email".$cptGestionnaire, $row) && !empty($row['gestionnaire_email'.$cptGestionnaire]) && $cptGestionnaire < 10) {
            $email = array_key_exists('gestionnaire_email'.$cptGestionnaire, $row) ? $row['gestionnaire_email'.$cptGestionnaire] : '';

            $userFound = $this->em->getRepository(User::class)->findOneBy(array('email' => $email));
            if (!empty($userFound)) {
                $userFound->addPossiblegroup($groupe);
                $users[] = $userFound;
                $this->em->persist($userFound);
            } else {
                $nom = array_key_exists('gestionnaire_nom'.$cptGestionnaire, $row) ? $row['gestionnaire_nom'.$cptGestionnaire] : '';
                $prenom = array_key_exists('gestionnaire_prenom'.$cptGestionnaire, $row) ? $row['gestionnaire_prenom'.$cptGestionnaire] : '';
                $phone = array_key_exists('gestionnaire_phone'.$cptGestionnaire, $row) ? $row['gestionnaire_phone'.$cptGestionnaire] : '';
                $mobile = array_key_exists('gestionnaire_mobile'.$cptGestionnaire, $row) ? $row['gestionnaire_mobile'.$cptGestionnaire] : '';
                $user = $this->userManager->createUser();
                $user->setConfirmationToken($this->tokenGenerator->generateToken());
                $user->setEnabled(true);
                $user->setPassword(md5(random_bytes(10)));
                $usergroupe = $this->em->getRepository(Usergroup::class)->findOneByName('Adhérent');
                $user->addPossiblegroup($usergroupe);
                $user->addPossiblegroup($groupe);
                $user->setGroups([$groupe]);
                $user->setFirstname($prenom);
                $user->setLastname($nom);
                $user->setPhone($phone);
                $user->setMobile($mobile);
                $user->setEmail($email);
                $user->setUsername($user->getName());
                $users[] = $user;
                $this->em->persist($user);
            }
            if ($this->sendemail) {
                $this->eventDispatcher->dispatch(MLCEvents::REGISTRATION_ADHERENT, new UserEvent($user, $this->getRequest()));
            }
            ++$cptGestionnaire;
        }
        return $users;
    }

    private function addSuccess($row, $line, $key, $message = '')
    {
        $csvline = implode(';', array_values($row));
        $this->success[$line][$key] = $message;
    }

    private function addError($row, $line, $key, $err = '')
    {
        $this->lineErrors[$line] = implode(';', array_values($row));
        $this->errors[$line][$key] = (array_key_exists($key, $row)?(!empty($row[$key])?'"'.$row[$key].'"':''):'').' ['.$err.']';
    }

    private function addWarning($row, $line, $key, $err = '')
    {
        $csvline = implode(';', array_values($row));
        if ($err == 'empty') {
            $errString = $this->translator->trans('Valeur vide !');
            $this->warnings[$line][$key] ='['.$errString.' ]';
        } elseif ($err == 'invalid') {
            $errString = $this->translator->trans('Valeur invalide !');
            $this->warnings[$line][$key] = '"'.(array_key_exists($key, $row)?$row[$key]:'').'" ['.$errString.']';
        } elseif ($err != '') {
            $this->warnings[$line][$key] = '"'.(array_key_exists($key, $row)?$row[$key]:'').'" ['.$err.']';
        }
    }

    private function tofloat($num)
    {
        $dotPos = strrpos($num, '.');
        $commaPos = strrpos($num, ',');
        $sep = (($dotPos > $commaPos) && $dotPos) ? $dotPos :
            ((($commaPos > $dotPos) && $commaPos) ? $commaPos : false);

        if (!$sep) {
            return floatval(preg_replace("/[^0-9]/", "", $num));
        }

        return floatval(
            preg_replace("/[^0-9]/", "", substr($num, 0, $sep)) . '.' .
            preg_replace("/[^0-9]/", "", substr($num, $sep+1, strlen($num)))
        );
    }
}
