<?php
namespace App\Controller;

use App\Entity\Adherent;
use App\Entity\Comptoir;
use App\Entity\Cotisation;
use App\Entity\Geoloc;
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\Enum\MoyenEnum;
use App\Form\Type\ImportFormType;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use FOS\UserBundle\Model\UserManagerInterface;
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;

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

    public function __construct(EntityManagerInterface $em, Security $security, UserManagerInterface $userManager, TranslatorInterface $translator)
    {
        $this->header = null;
        $this->warnings = array();
        $this->errors = array();
        $this->lineErrors = array();
        $this->em = $em;
        $this->security = $security;
        $this->userManager = $userManager;
        $this->translator = $translator;
        $this->siege = null;
    }

    public function listAction()
    {
        $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();

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

            $this->importFromCSV($media);

            if (empty($this->errors)) {
                $import = $this->em->getRepository(Import::class)->findOneById($idimport);
                $import->setEnabled(true);
                $this->em->persist($import);
                $this->em->flush();
                $this->addFlash(
                    'success',
                    'Import effectué avec succès !'
                );
            }
        }
        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($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 ($this->header == $config['adherent']['header']) {
            $result = $this->importAdherent($csvRows);
        } elseif ($this->header == $config['prestataire']['header']) {
            $result = $this->importPrestataire($csvRows);
        } elseif ($this->header == $config['groupe']['header']) {
            $result = $this->importGroupe($csvRows);
        } elseif ($this->header == $config['comptoir']['header']) {
            $result = $this->importComptoir($csvRows);
        } else {
            $this->errors['error'] = $this->translator->trans('CSV invalide');
        }

        return $result;
    }

    private function importComptoir($csvRows)
    {
        // Iterate over the reader and write each row to the database
        // groupe;name;content;phone;mobile;adresse;cpostal;ville;compte
        $line = 0;
        foreach ($csvRows as $row) {
            if ($line == 0) {
                $line++;
                continue;
            }
            $groupe = $row['groupe'];
            $name = $row['name'];
            $content = $row['content'];
            $phone = $row['phone'];
            $adresse = $row['adresse'];
            $cpostal = $row['cpostal'];
            $ville = $row['ville'];
            $compte = $row['compte'];

            if (empty($groupe) && empty($name)) {
                $this->addError($row, $line, 'nom & groupe', $this->translator->trans("Le nom et le groupe sont obligatoires !"));
                $line++;
                continue;
            }
            $comptoir = $this->em->getRepository(Comptoir::class)->findOneBy(array('name' => $name, 'groupe' => $groupe));
            if (empty($comptoir)) {
                $comptoir = new Comptoir();
                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);
                    }
                    $comptoir->setGroupe($groupeFound);
                }
                if (!empty($name)) {
                    $comptoir->setName($name);
                } else {
                    $this->addWarning($row, $line, 'name', 'empty');
                }
                if (!empty($content)) {
                    $comptoir->setContent($content);
                } else {
                    $this->addWarning($row, $line, 'content', 'empty');
                }
                if (!empty($phone)) {
                    $comptoir->setTel($phone);
                } else {
                    $this->addWarning($row, $line, 'phone', '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);
                    $comptoir->setGeoloc($geolocFound);
                }
                $this->addSuccess($row, $line, 'comptoir', $this->translator->trans('Comptoir ajouté : ').$name);
                $this->em->persist($comptoir);
                $this->em->flush();
                $this->em->clear();
            } else {
                $this->addError($row, $line, 'name', $this->translator->trans("Le comptoir avec ce nom '".$name."' existe déjà !"));
            }

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

    private function importGroupe($csvRows)
    {
        // Iterate over the reader and write each row to the database
        // name;content;compte
        $line = 0;
        foreach ($csvRows as $row) {
            if ($line == 0) {
                $line++;
                continue;
            }
            $name = $row['name'];
            $content = $row['content'];
            $compte = $row['compte'];
            $groupe = $this->em->getRepository(Groupe::class)->findOneBy(array('name' => $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');
                }
                $this->addSuccess($row, $line, 'groupe', $this->translator->trans('Groupe ajouté : ').$name);
                $this->em->persist($groupe);
                $this->em->flush();
                $this->em->clear();
            } else {
                $this->addError($row, $line, 'name', $this->translator->trans("Le groupe avec ce nom '".$name."' existe déjà !"));
            }
            $line++;
        }
        ksort($this->errors);
        ksort($this->warnings);
    }

    private function importPrestataire($csvRows)
    {
        // Iterate over the reader and write each row to the database
        // groupe;adresse;cpostal;ville;raison;metier;statut;responsable;iban;siret;web;compte;horaires;description;firstname;lastname;email;phone;mobile;rubriques
        $line = 0;
        foreach ($csvRows as $row) {
            $hasError = false;
            if ($line == 0) {
                $line++;
                continue;
            }
            $groupe = $row['groupe'];
            $adresse = $row['adresse'];
            $cpostal = $row['cpostal'];
            $ville = $row['ville'];
            $raison = $row['raison'];
            $metier = $row['metier'];
            $statut = $row['statut'];
            $responsable = $row['responsable'];
            $iban = $row['iban'];
            $siret = $row['siret'];
            $web = $row['web'];
            $compte = $row['compte'];
            $horaires = $row['horaires'];
            $description = $row['description'];
            $firstname = $row['firstname'];
            $lastname = $row['lastname'];
            $email = $row['email'];
            $phone = $row['phone'];
            $mobile = $row['mobile'];
            $rubriques = $row['rubriques'];

            $prestataire = new Prestataire();
            $user = $this->userManager->createUser();
            $user->setEnabled(true);
            $user->setPassword(md5(random_bytes(10)));
            $user->addRole('ROLE_PRESTATAIRE');
            $usergroupe = $this->em->getRepository(Usergroup::class)->findOneByName('Prestataire');
            $user->addGroup($usergroupe);
            $prestataire->addUser($user);

            if (!empty($raison)) {
                $prestataire->setRaison($raison);
            } else {
                $this->addWarning($row, $line, 'raison', 'empty');
            }
            if (!empty($metier)) {
                $prestataire->setMetier($metier);
            } else {
                $this->addWarning($row, $line, 'metier', 'empty');
            }
            if (!empty($statut)) {
                $prestataire->setStatut($statut);
            } else {
                $this->addWarning($row, $line, 'statut', 'empty');
            }
            if (!empty($responsable)) {
                $prestataire->setResponsable($responsable);
            } else {
                $this->addWarning($row, $line, 'responsable', 'empty');
            }
            if (!empty($iban)) {
                $prestataire->setIban($iban);
            } else {
                $this->addWarning($row, $line, 'iban', 'empty');
            }
            if (!empty($siret)) {
                $prestataire->setSiret($siret);
            } else {
                $this->addWarning($row, $line, 'siret', 'empty');
            }
            if (!empty($web)) {
                $prestataire->setWeb($web);
            } else {
                $this->addWarning($row, $line, 'web', 'empty');
            }
            if (!empty($horaires)) {
                $prestataire->setHoraires($horaires);
            } else {
                $this->addWarning($row, $line, 'horaires', 'empty');
            }
            if (!empty($description)) {
                $prestataire->setDescription($description);
            } else {
                $this->addWarning($row, $line, 'description', 'empty');
            }
            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++;
                    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($compte)) {
                $prestataire->setCompte($this->tofloat($compte));
            } else {
                $this->addWarning($row, $line, 'compte', '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);
                }
                $prestataire->setGroupe($groupeFound);
            } else {
                $this->addWarning($row, $line, 'groupe', 'empty');
            }
            if (!empty($rubriques)) {
                $rubriquesArray = explode(',', $rubriques);
                foreach ($rubriquesArray as $rubrique) {
                    $rubriquesFound = $this->em->getRepository(Rubrique::class)->findOneBy(array('name' => $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);
                    }
                }
            } else {
                $this->addWarning($row, $line, 'rubriques', 'empty');
            }
            if (!empty($adresse) || !empty($cpostal) || !empty($ville)) {
                $geolocFound = new Geoloc();
                $geolocFound->setAdresse($adresse);
                $geolocFound->setCpostal(intval($cpostal));
                $geolocFound->setVille($ville);
                $prestataire->setGeoloc($geolocFound);
            }
            if (!$hasError) {
                $this->addSuccess($row, $line, 'user', $this->translator->trans('Prestataire bien ajouté : ').$user->__toString());
                $this->userManager->updateUser($user);
                $this->em->persist($user);
                $this->em->flush();
                $this->em->clear();
            }

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

    private function importAdherent($csvRows)
    {
        // Iterate over the reader and write each row to the database
        // groupe;firstname;lastname;email;phone;mobile;adresse;cpostal;ville;ecompte
        $line = 0;
        foreach ($csvRows as $row) {
            $hasError = false;
            if ($line == 0) {
                $line++;
                continue;
            }
            $groupe = $row['groupe'];
            $firstname = $row['firstname'];
            $lastname = $row['lastname'];
            $email = $row['email'];
            $phone = $row['phone'];
            $mobile = $row['mobile'];
            $adresse = $row['adresse'];
            $cpostal = $row['cpostal'];
            $ville = $row['ville'];
            $ecompte = $row['ecompte'];
            $cotisations = $row['cotisations'];

            $adherent = new Adherent();
            $user = $this->userManager->createUser();
            $user->setEnabled(true);
            $user->setPassword(md5(random_bytes(10)));
            $user->addRole('ROLE_ADHERENT');
            $usergroupe = $this->em->getRepository(Usergroup::class)->findOneByName('Adherent');
            $user->addGroup($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++;
                    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 Cotisation();
                        $now = new DateTime();
                        $cotisation->setReference('Import du '.$now->format('d/m/Y H:i'));
                        $cotisation->setOperateur($user);
                        $cotisation->setExpediteur($adherent);
                        $cotisation->setMoyen(MoyenEnum::MOYEN_AUTRE);
                        $cotisationDetailsArray = explode(':', $cotisationDetails);
                        if (count($cotisationDetailsArray) == 1) {
                            $cotisation->setMontant(intval($cotisationDetails));
                            $cotisation->setDebut($now);
                            $cotisation->setFin(new DateTime('+ 1 year'));
                        } else {
                            $cotisation->setMontant(intval($cotisationDetailsArray[0]));
                            $cotisation->setAnnee(intval($cotisationDetailsArray[1]));
                            $cotisation->setDebut(DateTime::createFromFormat('Ymd', intval($cotisationDetailsArray[1]).'0101'));
                            $cotisation->setFin(DateTime::createFromFormat('Ymd', intval($cotisationDetailsArray[1]).'1231'));
                        }
                        $user->addCotisation($cotisation);
                        // $this->em->persist($cotisation);
                    }
                }
            } 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->userManager->updateUser($user);
                $this->em->persist($user);
                $this->em->flush();
                $this->em->clear();
            }

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

    private function parseCSV($filePath, $fileName, $ignoreFirstLine = false)
    {
        // $finder = new Finder();
        // $finder->files()
        //     ->in($filePath)
        //     ->name($fileName)
        // ;
        // foreach ($finder as $file) {
        //     $csv = $file;
        // }
        $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 !"));
                        continue;
                    }
                    $rows[] = array_combine(array_values($firstline), array_values($data));
                }
            }
            fclose($handle);
        }
        return $rows;
    }

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

    private function addError($row, $line, $key, $err = '')
    {
        $this->lineErrors[$line] = implode(';', array_values($row));
        $this->errors[$line][$key][$this->lineErrors[$line]] = '"'.(array_key_exists($key, $row)?$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][$csvline] ='['.$errString.' ]';
        } elseif ($err == 'invalid') {
            $errString = $this->translator->trans('Valeur invalide !');
            $this->warnings[$line][$key][$csvline] = '"'.(array_key_exists($key, $row)?$row[$key]:'').'" ['.$errString.']';
        } elseif ($err != '') {
            $this->warnings[$line][$key][$csvline] = '"'.(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)))
        );
    }
}
