<?php

namespace App\Controller;

use App\Entity\AccountComptoir;
use App\Entity\AccountGroupe;
use App\Entity\AccountPrestataire;
use App\Entity\AccountSiege;
use App\Entity\Caissier;
use App\Entity\Comptoir;
use App\Entity\Geoloc;
use App\Entity\GeolocPrestataire;
use App\Entity\GlobalParameter;
use App\Entity\Groupe;
use App\Entity\Prestataire;
use App\Entity\SelfEvalPrestaQuiz;
use App\Entity\Siege;
use App\Entity\User;
use App\Entity\Usergroup;
use App\Enum\CurrencyEnum;
use App\Form\Type\DistributorSelfEvalPrestaQuizType;
use App\Form\Type\SelfEvalPrestaQuizType;
use App\Form\Type\InfosPrestaQuizType;
use App\Form\Type\InstallFormType;
use App\Form\Type\ProducerSelfEvalPrestaQuizType;
use App\Security\LoginAuthenticator;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityManagerInterface;
use FOS\UserBundle\Event\FilterUserResponseEvent;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Model\UserManagerInterface;
use Geocoder\Provider\Nominatim\Nominatim;
use Geocoder\Query\GeocodeQuery;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Security as Secur;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken;

class IndexController extends AbstractController
{
    private $eventDispatcher;
    private $em;
    private $userManager;
    private $tokenManager;
    private $guard;
    private $authenticator;
    private $session;
    private $tokenStorage;
    private $router;
    private $security;
    private $mailer;

    public function __construct(
        EventDispatcherInterface $eventDispatcher,
        EntityManagerInterface $em,
        UserManagerInterface $userManager,
        CsrfTokenManagerInterface $tokenManager = null,
        GuardAuthenticatorHandler $guard,
        SessionInterface $session,
        LoginAuthenticator $authenticator,
        TokenStorageInterface $tokenStorage,
        RouterInterface $router,
        Secur $security,
        \Swift_Mailer $mailer
    ) {
        $this->eventDispatcher = $eventDispatcher;
        $this->em = $em;
        $this->userManager = $userManager;
        $this->tokenManager = $tokenManager;
        $this->guard = $guard;
        $this->authenticator = $authenticator;
        $this->session = $session;
        $this->tokenStorage = $tokenStorage;
        $this->router = $router;
        $this->security = $security;
        $this->mailer = $mailer;
    }

    /**
     * @Route("/", name="index")
     */
    public function index(Request $request)
    {
        /* Pour la première installation */
        $siege = $this->em->getRepository(Siege::class)->findAll();
        if (null == $siege || empty($siege)) {
            return $this->redirectToRoute('installation');
        }
        /* Pour la modale de login => SecurityController loginAction */
        $session = $request->getSession();
        $lastUsernameKey = Secur::LAST_USERNAME;
        // last username entered by the user
        $lastUsername = (null === $session) ? '' : $session->get($lastUsernameKey);
        $csrfToken = $this->tokenManager
            ? $this->tokenManager->getToken('authenticate')->getValue()
            : null;

        $isWordpress = 'false' != $this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::USE_WORDPRESS);

        $template = 'index.html.twig';
        if ($isWordpress) {
            $template = 'index_wordpress.html.twig';
        }

        if (
            isset($_GET['upswd'])
            && 'success' == $_GET['upswd']
            && $this->security->isGranted('ROLE_ADHERENT')
            && true == $this->getParameter('tav_env')
        ) {
            $this->addFlash(
                'warning',
                'Suite à la modification de votre mot de passe, pensez à mettre à jour votre code de paiment pour pouvoir effectuer des achats.'
            );
        }

        // Redirect prestaire at login if self evaluation process is enabled
        list($redirRoute,$presta) = $this->getAndDispatchPrestataire('index');
        if ($redirRoute) {
            return $this->redirectToRoute($redirRoute);
        }

        return $this->render('@kohinos/' . $template, [
            'news' => [],
            'last_username' => $lastUsername,
            'csrf_token' => $csrfToken,
        ]);
    }

    /**
     * This method is core method to guide the new
     * prestataire when using presta_self_init_and_eval mode.
     * It is basically a state machine that returns the appropriate
     * route path depending on current state (details in the body).
     * It also provides the prestataire object itself for convenience
     * (not so easy to fetch from user when prestataire is disabled).
     * @param $origin
     * @return array|null[]
     */
    private function getAndDispatchPrestataire($origin)
    {
        /*
         * Go to home page if process is not active or if user is not connected as adherent.
         * Second filter is just an easy way to early detect anonymous users.
         * Why adherent ? Because once prestataire is created disabled and after is has been linked
         * to some user, this user will connect to the platform as an adherent (because prestataire is disabled).
         */
        if (!$this->getParameter('presta_self_init_and_eval')
            || !$this->security->isGranted('ROLE_ADHERENT')) { //not sure ROLE_ADHERENT is the proper check here
            $destination = 'index';
            return $destination !== $origin ? [$destination, null] : [null, null];
        }

        //Fetch the prestataire even when it is disabled
        /* @var ArrayCollection $prestas */
        if ($this->em->getFilters()->isEnabled('enabled_filter')) {
            $this->em->getFilters()->disable('enabled_filter');
        }
        $prestas = $this->em->getRepository(Prestataire::class)->findByData(['user' => $this->security->getUser()]);
        if (!$this->em->getFilters()->isEnabled('enabled_filter')) {
            $this->em->getFilters()->enable('enabled_filter');
        }

        if (!$prestas) {
            $destination = 'index';
            return $destination !== $origin ? [$destination, null] : [null, null];
        }

        /* @var Prestataire $presta */
        $presta = $prestas[0];

        //Go to home page if the prestataire is enabled
        if ($presta->isEnabled()) {
            $destination = 'index';
            return $destination !== $origin ? [$destination, $presta] : [null, $presta];
        }

        //Go to "all forms already sent, please just wait now" page if questionnaire has been submitted already
        $prestaQuizz = $presta->getSelfEvalPrestaQuiz();
        if ($prestaQuizz && $prestaQuizz->isSubmitted) {
            $destination = 'prestaquiz-sent';
            return $destination !== $origin ? [$destination, $presta] : [null, $presta];
        }

        //Go to questionnaire if first identification form has been submitted
        //Condition is detected using market_channel_function attributes which is
        //set in the first form (it is also the only mandatory attribute to start
        //the self-eval part)
        if ($presta->getMarketChannelFunction()) {
            $destination = 'prestaquiz-selfeval';
            return $destination !== $origin ? [$destination, $presta] : [null, $presta];
        }

        //In other cases, start the subscription process
        $destination = 'prestaquiz-infos';
        return $destination !== $origin ? [$destination, $presta] : [null, $presta];
    }

    /**
     * @Route("/prestaquiz-sent", name="prestaquiz-sent")
     */
    public function prestaQuizSent(Request $request): Response
    {
        list($redirRoute,$presta) = $this->getAndDispatchPrestataire('prestaquiz-sent');
        if ($redirRoute) {
            return $this->redirectToRoute($redirRoute);
        }

        return $this->render('@kohinos/tav/prestaquiz/sent.html.twig');
    }

    /**
     * @Route("/manifest.json", name="manifest")
     */
    public function manifest(Request $request)
    {
        return new JsonResponse(json_decode($this->renderView('@kohinos/common/manifest.json.twig')));
    }

    /**
     * @Route("/cotisation/invalid", name="cotisation_invalid")
     */
    public function cotisationInvalidAction(Request $request)
    {
        return $this->render('@kohinos/index_cotisation_invalid.html.twig', []);
    }

    /**
     * @Route("/prestaquiz-infos", name="prestaquiz-infos")
     */
    public function prestaQuizInfosAction(Request $request): Response
    {
        list($redirRoute,$presta) = $this->getAndDispatchPrestataire('prestaquiz-infos');
        if ($redirRoute) {
            return $this->redirectToRoute($redirRoute);
        }

        /* @var Prestataire $presta */

        if(!$presta->getGeolocs() || $presta->getGeolocs()->isEmpty()) {
            $address = new GeolocPrestataire();
            $presta->addGeoloc($address);
        }
        $form = $this->createForm(InfosPrestaQuizType::class, $presta);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $this->security->getUser()->setFirstName($form->get('userFirstName')->getData());
            $this->security->getUser()->setLastName($form->get('userLastName')->getData());
            $this->em->flush();
            list($redirRoute,$presta) = $this->getAndDispatchPrestataire('prestaquiz-infos');
            if ($redirRoute) {
                return $this->redirectToRoute($redirRoute);
            }
        }

        return $this->render('@kohinos/tav/prestaquiz/infos.html.twig', [
            'form' => $form->createView(),
        ]);
    }

    /**
     * @Route("/prestaquiz-selfeval", name="prestaquiz-selfeval")
     */
    public function prestaQuizSelfEvalAction(Request $request): Response
    {
        list($redirRoute,$presta) = $this->getAndDispatchPrestataire('prestaquiz-selfeval');
        if ($redirRoute) {
            return $this->redirectToRoute($redirRoute);
        }

        /* @var Prestataire $presta */

        $formClass = Prestataire::DISTRIBUTOR === $presta->getMarketChannelFunction() ?
            DistributorSelfEvalPrestaQuizType::class : ProducerSelfEvalPrestaQuizType::class;

        // Get existing form if it has been saved for future completion
        $quiz = $presta->getSelfEvalPrestaQuiz();
        if (null == $quiz) {
            $quiz = new SelfEvalPrestaQuiz();
        }

        $form = $this->createForm($formClass, $quiz);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $definitiveSubmit = $form->get('submit_presta_quizz')->getData() == '1';
            $presta->setSelfEvalPrestaQuiz($quiz);
            $quiz->isSubmitted = $definitiveSubmit;
            $this->em->persist($quiz);
            $this->em->flush();

            //Send email !
            if ($definitiveSubmit) {
                $this->notifyGestionnaireAfterPrestaquizSent($presta);
            }

            list($redirRoute,$presta) = $this->getAndDispatchPrestataire('prestaquiz-selfeval');
            if ($redirRoute) {
                return $this->redirectToRoute($redirRoute);
            }
        }
 
        $tmpl = Prestataire::DISTRIBUTOR === $presta->getMarketChannelFunction() ?
            '@kohinos/tav/prestaquiz/distributor.html.twig'
            : '@kohinos/tav/prestaquiz/producer.html.twig';

        return $this->render($tmpl, [
            'form' => $form->createView(),
        ]);
    }

    /**
     * @Route("/quiz-presta-edit", name="quiz-presta-edit")
     * @Security("is_granted('ROLE_PRESTATAIRE')")
     */
    /* Late presta edit (after definitive submission) :
     * As we are not in the somehow complex subscription tunnel context anymore,
     * I found easier to create a new controller
     * instead of reusing prestaQuizSelfEvalAction
     */
    public function quizPrestaEditAction(Request $request): Response
    {
        //Look for enabled prestataire with current user (fail if no enabled prestataire has been found)
        $prestas = $this->em->getRepository(Prestataire::class)->findByData(['user' => $this->security->getUser()]);
        if(!$prestas) {
            throw new \Exception('Vous n\'êtes pas autorisé à accèder à cette page.');
        } else {
            $presta = $prestas[0];
        }

        /* @var Prestataire $presta */

        $formClass = Prestataire::DISTRIBUTOR === $presta->getMarketChannelFunction() ?
            DistributorSelfEvalPrestaQuizType::class : ProducerSelfEvalPrestaQuizType::class;

        // Block if no quiz (e.g. user access this controleur in non presta_self_init_and_eval env) or
        // if presta has been enabled while questionnaire has not been submitted yet (should not occur)
        $quiz = $presta->getSelfEvalPrestaQuiz();
        if (null == $quiz || !$quiz->isSubmitted) {
            throw new \Exception('Aucun questionnaire soumis trouvé.');
        }

        $form = $this->createForm($formClass, $quiz, ["mode" => SelfEvalPrestaQuizType::PRESTA_EDIT_AFTER_DEFINITIVE_SUBMISSION]);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $this->em->flush();

            // Notify group administrator that the user updated their self evaluation
            $this->notifyGestionnaireAfterPrestaquizSent($presta, 'edit');

            $this->addFlash(
                'success',
                'Modifications enregistrées !'
            );

            return $this->redirectToRoute('show_prestataire', ['slug' => $presta->getSlug()]);
        }

        $tmpl = Prestataire::DISTRIBUTOR === $presta->getMarketChannelFunction() ?
            '@kohinos/tav/prestaquiz/distributor.html.twig'
            : '@kohinos/tav/prestaquiz/producer.html.twig';

        return $this->render($tmpl, [
            'form' => $form->createView(),
        ]);
    }

    /**
     * Notify by email the group administrator when a prestataire submits or edits their
     * self evaluation quizz.
     *   
     * @param $type submit|edit, action performed by the user. Default: 'submit'.
     */
    private function notifyGestionnaireAfterPrestaquizSent(Prestataire $presta, $type="submit")
    {
        $user = $this->security->getUser();
        $globalParamRepo = $this->em->getRepository(GlobalParameter::class);
        $to = $globalParamRepo->getMailOfGestionnaireDeGroupeOrDefaultContact($presta);

        $subjectKeyWord = $type == "submit" ? "Réception" : "Édition";
        $subject = $globalParamRepo->val(GlobalParameter::MLC_NAME_SMALL)
            . ' : ' . $subjectKeyWord . ' d\'un questionnaire de point de vente';

        $contentKeyWord = $type == "submit" ? "soumettre" : "modifier";
        $mail = (new \Swift_Message($subject))
            ->setFrom($globalParamRepo->val(GlobalParameter::MLC_NOTIF_EMAIL)) //using $from here sometimes fails with 550 5.7.1 Sender mismatch
            ->setTo($to)
            ->setBody("L'utilisateur " . $user->getUsername()
                . " vient de " . $contentKeyWord . " un questionnaire d'auto-évaluation pour le point de vente " . $presta->getRaison() . "."
            );
        $this->mailer->send($mail);
    }

    /**
     * @Route("/installation", name="installation")
     */
    public function installationAction(Request $request)
    {
        $siege = $this->em->getRepository(Siege::class)->findAll();
        if (!empty($siege) && count($siege) > 0) {
            // Installation déjà effectuée !
            return $this->redirectToRoute('index');
        }
        $repogroup = $this->em->getRepository(Usergroup::class);
        $group = $repogroup->findOneBy(['name' => 'Super Admin']);
        if (empty($group)) {
            return new Response('ERREUR !<br><br>Avant de pouvoir installer le kohinos, il faut charger les fixtures : <br>Soit charger les fixtures standards :<br><em>php bin/console hautelook:fixtures:load --purge-with-truncate</em><br/><br/>Soit charger les fixtures de dev (pour tester le kohinos avec des données/comptes factices):<br/><em>php bin/console hautelook:fixtures:load --purge-with-truncate --env=test</em>', 200);
        }
        $user = $this->userManager->createUser();
        $user->setEnabled(true);
        $user->addPossiblegroup($group);
        $user->addGroup($group);
        $form = $this->createForm(InstallFormType::class, ['user' => $user]);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $siege = $form['siege']->getData();
            $user = $form['user']->getData();
            $groupe = $form['groupe']->getData();
            $configs = $form['config']->getData();
            $iban = $form['iban']->getData();
            $website = $form['website']->getData();
            $groupe->setSiege($siege);

            //Création du prestataire recevant les cotisations
            $presta = new Prestataire();
            $presta->setMlc(true);
            $presta->setRaison('Monnaie locale');
            $presta->setDescription('Association gérant la monnaie locale et recevant les cotisations');
            $presta->setEnabled(true);
            $presta->setIban($iban);
            if (!empty($website)) {
                $presta->setWeb($website);
            }
            $presta->setSiret(' ');
            $groupePresta = $repogroup->findOneBy(['name' => 'Prestataire']);
            $user->addPossiblegroup($groupePresta);
            $presta->addUser($user);
            $presta->setGroupe($groupe);

            $this->em->persist($siege);

            if (null == $siege->getAccountWithCurrency(CurrencyEnum::CURRENCY_EMLC)) {
                $account = new AccountSiege();
                $account
                    ->setBalance($siege->getEcompteNantie())
                    ->setCurrency(CurrencyEnum::CURRENCY_EMLC) // compte de monnaie numérique nantie
                ;
                $siege->addAccount($account);
                $this->em->persist($account);
            }
            if (null == $siege->getAccountWithCurrency(CurrencyEnum::CURRENCY_MLC)) {
                $accountMLC = new AccountSiege();
                $accountMLC
                    ->setBalance($siege->getCompte())
                    ->setCurrency(CurrencyEnum::CURRENCY_MLC) // compte de monnaie papier au siege non nantie
                ;
                $siege->addAccount($accountMLC);
                $this->em->persist($accountMLC);
            }
            // if ($siege->getAccountWithCurrency(CurrencyEnum::CURRENCY_EURO) == null) {
            //     $accountEuro = new AccountSiege();
            //     $accountEuro
            //         ->setCurrency(CurrencyEnum::CURRENCY_EURO) // compte réel de nantissement // @TODO : offrir la possibilité de voir les opérations du compte euro du fond de garanti ? Ou plutôt de suivre les euros à récupérés aux comptoirs
            //     ;
            //     $siege->addAccount($accountEuro);
            //     $this->em->persist($accountEuro);
            // }
            if (null == $siege->getAccountWithCurrency(CurrencyEnum::CURRENCY_MLC_NANTIE)) {
                $accountMlcNantie = new AccountSiege();
                $accountMlcNantie
                    ->setBalance($siege->getCompteNantie())
                    ->setCurrency(CurrencyEnum::CURRENCY_MLC_NANTIE) // compte de monnaie papier nantie
                ;
                $siege->addAccount($accountMlcNantie);
                $this->em->persist($accountMlcNantie);
                $this->em->persist($siege);
            }

            $this->em->persist($groupe);
            if (null == $groupe->getAccountWithCurrency(CurrencyEnum::CURRENCY_MLC)) {
                $account = new AccountGroupe();
                $account
                    ->setBalance($groupe->getCompte())
                    ->setCurrency(CurrencyEnum::CURRENCY_MLC)
                ;
                $groupe->addAccount($account);
                $this->em->persist($account);
                $this->em->persist($groupe);
            }
            $comptoirData = $form['comptoir']->getData();
            $comptoirName = $comptoirData['name'];
            $comptoirCompte = $comptoirData['compte'];
            $comptoirGeoloc = $comptoirData['geoloc'];
            if (!empty($comptoir)) {
                $comptoir = new Comptoir();
                $comptoir->setName($comptoirName);
                $comptoir->setEnabled(true);
                $comptoir->setGeoloc($comptoirGeoloc);
                $comptoir->setGroupe($groupe);
                $this->em->persist($comptoir);
                if (null == $comptoir->getAccountWithCurrency(CurrencyEnum::CURRENCY_MLC)) {
                    $account = new AccountComptoir();
                    $account
                        ->setBalance(empty($comptoirCompte) ? $comptoirCompte : 0)
                        ->setCurrency(CurrencyEnum::CURRENCY_MLC)
                    ;
                    $comptoir->addAccount($account);
                    $this->em->persist($account);
                    $this->em->persist($comptoir);
                }
            }
            $this->userManager->updateUser($user);
            $this->em->persist($presta);
            if (null == $presta->getAccountWithCurrency(CurrencyEnum::CURRENCY_EMLC)) {
                $account = new AccountPrestataire();
                $account
                    ->setBalance($presta->getEcompte())
                    ->setCurrency(CurrencyEnum::CURRENCY_EMLC)
                ;
                $presta->addAccount($account);
                $this->em->persist($account);
            }
            if ($presta->isMlc() && null == $presta->getAccountWithCurrency(CurrencyEnum::CURRENCY_EURO)) {
                // Ajout du compte de fonctionnement euro pour le prestataire MLC
                $accountEuro = new AccountPrestataire();
                $accountEuro
                        ->setCurrency(CurrencyEnum::CURRENCY_EURO)
                    ;
                $presta->addAccount($accountEuro);
                $this->em->persist($accountEuro);
            }
            $this->em->persist($presta);
            foreach ($configs as $config) {
                $this->em->persist($config);
            }
            $this->em->flush();
            // Création de l'utilisateur avec ROLE_API pour le plugin Wordpress ou les applis tierces !
            $userAPI = $this->userManager->createUser();
            $userAPI->setEnabled(true);
            $userAPI->setUsername('userapi');
            $userAPI->setPassword(md5(random_bytes(10)));
            $userAPI->setEmail('userapi@kohinos.fr');
            $userAPI->addRole('ROLE_API');
            $this->userManager->updateUser($userAPI);

            $this->em->flush();
            $this->addFlash(
                'success',
                "BRAVO ! Vous venez de configurer le Kohinos. Vous pouvez maintenant accéder à l'interface d'administration."
            );

            $url = $this->generateUrl('fos_user_registration_confirmed');
            $response = new RedirectResponse($url);

            // @TODO : send mail with this event catch !
            $this->eventDispatcher->dispatch(FOSUserEvents::REGISTRATION_COMPLETED, new FilterUserResponseEvent($user, $request, $response));

            return new RedirectResponse($this->generateUrl('sonata_admin_dashboard'));
        }

        return $this->render('@kohinos/installation.html.twig', [
            'form' => $form->createView(),
        ]);
    }

    /**
     * @Route("/geoloc", name="geolocAdresse")
     */
    public function geoLocAction(Request $request)
    {
        $referer = $request->headers->get('referer');
        if ($referer && !$request->isXmlHttpRequest()) {
            return $this->redirect($referer);
        } elseif (!$request->isXmlHttpRequest()) {
            return new Response('', Response::HTTP_BAD_REQUEST);
        }
        $status = 'success';
        $return = null;
        if (!empty($request->get('cpostal')) && !empty($request->get('ville'))) {
            try {
                // GEOCODING ADDRESS :
                $httpClient = new \Http\Adapter\Guzzle6\Client();
                $provider = Nominatim::withOpenStreetMapServer($httpClient, 'Mozilla/5.0');
                $geocoder = new \Geocoder\StatefulGeocoder($provider, 'fr');
                // Query geocoding from complete address
                $result = $geocoder->geocodeQuery(GeocodeQuery::create($request->get('adresse') . ' ' . $request->get('cpostal') . ' ' . $request->get('ville')));
                if (count($result) > 0) {
                    $coords = $result->first()->getCoordinates();
                    $return = ['lat' => $coords->getLatitude(), 'lon' => $coords->getLongitude()];
                } else {
                    $result = $geocoder->geocodeQuery(GeocodeQuery::create($request->get('cpostal') . ' ' . $request->get('ville')));
                    if (count($result) > 0) {
                        $coords = $result->first()->getCoordinates();
                        $return = ['lat' => $coords->getLatitude(), 'lon' => $coords->getLongitude()];
                    }
                }
            } catch (\Exception $e) {
                $status = 'error';
            }
        } else {
            $status = 'error';
        }

        return new JsonResponse(['status' => $status, 'data' => $return]);
    }

    /**
     * Choix du groupe local géré.
     *
     * @Route("/login/groupe/choice/{usergrpid}/{grpid}", name="groupe_choice")
     * @ParamConverter("group", class="App:Usergroup", options={"mapping": {"usergrpid": "id"}})
     * @ParamConverter("groupe", class="App:Groupe", options={"mapping": {"grpid": "id"}})
     * @IsGranted("ROLE_USER")
     */
    public function groupeChoiceAction(Usergroup $group, Groupe $groupe, Request $request)
    {
        $this->em->refresh($this->getUser());
        if (!($this->getUser()->getPossiblegroups()->exists(function ($key, $value) {
            return in_array('ROLE_GESTION_GROUPE', $value->getRoles()) || in_array('ROLE_CONTACT', $value->getRoles()) || in_array('ROLE_TRESORIER', $value->getRoles());
        }) and $this->getUser()->getGroupesgeres()->contains($groupe))) {
            $this->addFlash(
                'error',
                'Accès impossible !'
            );

            return $this->redirectToRoute('index');
        }
        $this->removeOldSessionParams();
        // On enregistre le groupe local choisit en session
        $this->session->set('_groupegere', $groupe);
        $this->reloadUserTokenFromGroup($group, $request);

        if (true == $this->getParameter('tav_env')) {
            return $this->redirectToRoute('index');
        } else {
            return $this->redirectToRoute('sonata_admin_dashboard');
        }
    }

    /**
     * Choix du comptoir géré.
     *
     * @Route("/login/comptoir/choice/{usergrpid}/{cptid}", name="comptoir_choice")
     * @ParamConverter("group", class="App:Usergroup", options={"mapping": {"usergrpid": "id"}})
     * @ParamConverter("comptoir", class="App:Comptoir", options={"mapping": {"cptid": "id"}})
     * @IsGranted("ROLE_USER")
     */
    public function comptoirChoiceAction(Usergroup $group, Comptoir $comptoir, Request $request)
    {
        $this->em->refresh($this->getUser());
        if (!($this->getUser()->getPossiblegroups()->exists(function ($key, $value) {
            return in_array('ROLE_COMPTOIR', $value->getRoles());
        }) and $this->getUser()->getComptoirsgeres()->contains($comptoir))) {
            $this->addFlash(
                'error',
                'Accès impossible !'
            );

            return $this->redirectToRoute('index');
        }
        $this->removeOldSessionParams();
        // On enregistre le comptoir choisit en session
        $this->session->set('_comptoirgere', $comptoir);
        $this->reloadUserTokenFromGroup($group, $request);

        if (true == $this->getParameter('tav_env')) {
            return $this->redirectToRoute('index');
        } else {
            return $this->redirectToRoute('sonata_admin_dashboard');
        }
    }

    /**
     * Choix du presta géré.
     *
     * @Route("/login/presta/choice/{usergrpid}/{prestaid}", name="presta_choice")
     * @ParamConverter("group", class="App:Usergroup", options={"mapping": {"usergrpid": "id"}})
     * @ParamConverter("prestataire", class="App:Prestataire", options={"mapping": {"prestaid": "id"}})
     * @IsGranted("ROLE_USER")
     */
    public function prestaChoiceAction(Usergroup $group, Prestataire $prestataire, Request $request)
    {
        $this->em->refresh($this->getUser());

        // prevent operation if user isn't a prestataire
        if (
            !($this->getUser()->getPossiblegroups()->exists(function ($key, $value) {
                return in_array('ROLE_PRESTATAIRE', $value->getRoles());
            }) and $this->getUser()->getPrestataires()->contains($prestataire))
        ) {
            $this->addFlash(
                'error',
                'Accès impossible !'
            );

            return $this->redirectToRoute('index');
        }
        $this->removeOldSessionParams();
        // On enregistre le presta choisit en session
        $this->session->set('_prestagere', $prestataire);
        $this->reloadUserTokenFromGroup($group, $request);

        return $this->redirectToRoute('index');
    }

        /**
     * Choix du presta géré.
     *
     * @Route("/login/caissier/choice/{usergrpid}/{caissierid}", name="caissier_choice")
     * @ParamConverter("group", class="App:Usergroup", options={"mapping": {"usergrpid": "id"}})
     * @ParamConverter("caissier", class="App:Caissier", options={"mapping": {"caissierid": "id"}})
     * @IsGranted("ROLE_USER")
     */
    public function caissierChoiceAction(Usergroup $group, Caissier $caissier, Request $request)
    {
        $this->em->refresh($this->getUser());

        // prevent operation if user isn't a caissier
        if (
            !($this->getUser()->getPossiblegroups()->exists(function ($key, $value) {
                return in_array('ROLE_CAISSIER', $value->getRoles());
            }) and $this->getUser()->getCaissiers()->contains($caissier))
        ) {
            $this->addFlash(
                'error',
                'Accès impossible !'
            );

            return $this->redirectToRoute('index');
        }
        $this->removeOldSessionParams();
        // On enregistre le presta choisit en session
        $this->session->set('_prestagere', $caissier->getPrestataire());
        $this->reloadUserTokenFromGroup($group, $request);

        return $this->redirectToRoute('index');
    }

    /**
     * @Route("/login/group/choice/{id}", name="usergroup_choice")
     * @IsGranted("ROLE_USER")
     */
    public function groupChoiceAction(Usergroup $group, Request $request)
    {
        if (!$this->getUser()->getPossiblegroups()->contains($group)) {
            $this->addFlash(
                'error',
                'Accès impossible !'
            );

            return $this->redirectToRoute('index');
        }
        $this->removeOldSessionParams();
        $this->reloadUserTokenFromGroup($group, $request);

        if (in_array('ROLE_SUPER_ADMIN', $this->getUser()->getRoles(), true)) {
            // c'est un administrateur
            return new RedirectResponse($this->router->generate('sonata_admin_dashboard'));
        }

        return new RedirectResponse($this->router->generate('index'));
    }

    private function removeOldSessionParams()
    {
        $this->session->remove('_choixGroup');
        $this->session->remove('_groupId');
        $this->session->remove('_groupegere');
        $this->session->remove('_comptoirgere');
        $this->session->remove('_prestagere');
    }

    private function reloadUserTokenFromGroup($group, $request)
    {
        $groups = [$group];
        if ($this->getUser()->getGroups() !== $groups) {
            $this->getUser()->setGroups($groups);
            $this->em->persist($this->getUser());
            $this->em->flush();
        }
        $token = new PostAuthenticationGuardToken(
            $this->getUser(),
            'main',
            $this->getUser()->getRoles()
        );
        $this->tokenStorage->setToken($token);
        $this->guard->authenticateUserAndHandleSuccess(
            $this->getUser(),
            $request,
            $this->authenticator,
            'main'
        );
        $this->session->set('_security_main', serialize($token));
    }
}
