<?php

namespace App\Controller;

use App\Entity\AchatMonnaieAdherent;
use App\Entity\AchatMonnaiePrestataire;
use App\Entity\Adherent;
use App\Entity\CotisationAdherent;
use App\Entity\CotisationPrestataire;
use App\Entity\Don;
use App\Entity\Flux;
use App\Entity\Geoloc;
use App\Entity\GlobalParameter;
use App\Entity\Groupe;
use App\Entity\Payment;
use App\Entity\Prestataire;
use App\Entity\Siege;
use App\Entity\User;
use App\Entity\Usergroup;
use App\Enum\CurrencyEnum;
use App\Flux\FluxInterface;
use App\Security\LoginAuthenticator;
use App\Utils\CustomEntityManager;
use App\Utils\OperationUtils;
use App\Utils\TAVCotisationUtils;
use FOS\UserBundle\Model\UserManagerInterface;
use FOS\UserBundle\Util\TokenGeneratorInterface;
use Gamez\Symfony\Component\Serializer\Normalizer\UuidNormalizer;
use Payum\Core\Payum;
use Payum\Core\Request\GetHumanStatus;
use Payum\Core\Request\Notify;
use Sonata\Exporter\Handler;
use Sonata\Exporter\Source\DoctrineORMQuerySourceIterator;
use Sonata\Exporter\Writer\CsvWriter;
use Sonata\Exporter\Writer\JsonWriter;
use Sonata\Exporter\Writer\XlsWriter;
use Sonata\Exporter\Writer\XmlWriter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\Form;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Twig\Environment;

/**
 * Types de transfert : (Les transferts dans la structure sont les flux de billets détenus par les opérateurs.).
 *
 *  - SIEGE             =>     GROUPES LOCAUX           (Transfert du siège au groupe)
 *  - GROUPE            =>     SIEGE                    (Transfert du groupe au siège)
 *  - GROUPES LOCAUX    =>     COMPTOIRS                (Transfert du groupe au comptoir)
 *  - COMPTOIRS         =>     GROUPES LOCAUX           (Transfert du comptoir au groupe)
 *  - COMPTOIRS         =>     ADHERENTS                (Diffusion de monnaie papier auprès des adhérents)
 *  - COMPTOIRS         =>     PRESTATAIRES             (Diffusion de monnaie papier auprès des prestataires)
 *  - PRESTATAIRES      =>     COMPTOIRS                (Reconversion)
 *
 * Types de transaction :
 *
 *   - PRESTATAIRES     =>    ADHERENTS         (Virement vers un adherent)
 *   - PRESTATAIRES     =>    PRESTATAIRES      (Virement entre prestataires)
 *   - ADHERENTS        =>    PRESTATAIRES      (Paiement numérique)
 *   - SIEGE            =>    ADHERENTS         (Achat de monnaie numérique par CB d'un adhérent)
 *   - SIEGE            =>    PRESTATAIRES      (Achat de monnaie numérique par CB d'un prestataire)
 *   - COMPTOIR         =>    ADHERENTS         (Vente de monnaie numérique à un adhérent)
 *   - COMPTOIR         =>    PRESTATAIRES      (Vente de monnaie numérique à un prestataire)
 *   Changes :
 *   - COMPTOIR         =>    ADHERENTS         (Retrait de monnaie numérique en échange de billets MLC à un adhérent)
 *   - COMPTOIR         =>    PRESTATAIRES      (Retrait de monnaie numérique en échange de billets MLC à un prestataire)
 */
class FluxController extends AbstractController
{
    protected $em;
    protected $security;
    protected $translator;
    protected $eventDispatcher;
    protected $session;
    protected $payum;
    protected $authenticator;
    protected $guardHandler;
    protected $userManager;
    protected $templating;
    protected $operationUtils;
    protected $tokenGenerator;
    protected $validator;

    public function __construct(
        Security $security,
        CustomEntityManager $em,
        TranslatorInterface $translator,
        EventDispatcherInterface $eventDispatcher,
        SessionInterface $session,
        LoginAuthenticator $authenticator,
        GuardAuthenticatorHandler $guardHandler,
        UserManagerInterface $userManager,
        Payum $payum,
        Environment $templating,
        OperationUtils $operationUtils,
        TAVCotisationUtils $tavCotisationsUtils,
        TokenGeneratorInterface $tokenGenerator,
        ValidatorInterface $validator,
        CsrfTokenManagerInterface $tokenManager
    ) {
        $this->security = $security;
        $this->em = $em;
        $this->translator = $translator;
        $this->eventDispatcher = $eventDispatcher;
        $this->session = $session;
        $this->payum = $payum;
        $this->authenticator = $authenticator;
        $this->guardHandler = $guardHandler;
        $this->userManager = $userManager;
        $this->templating = $templating;
        $this->operationUtils = $operationUtils;
        $this->tokenGenerator = $tokenGenerator;
        $this->validator = $validator;
        $this->tokenManager = $tokenManager;
        $this->tavCotisationsUtils = $tavCotisationsUtils;
    }

    protected function manageFluxForm(Request $request, Form $form, $template = '@kohinos/flux/transaction.html.twig', $params = [])
    {
        // @TODO : better error management !! => show error on form instead of exception !
        if (null == $this->security->getUser()) {
            throw new \Exception('[FLUX] Opération impossible ! Utilisateur déconnecté !');
            // return new Response('Opération impossible ! Vous êtes déconnecté !', Response::HTTP_BAD_REQUEST);
        }
        // if (!$request->isXmlHttpRequest()) {
        //     return new Response('', Response::HTTP_BAD_REQUEST);
        // }
        $form->handleRequest($request);
        if ($form->isSubmitted() && !$this->tokenManager->isTokenValid($this->tokenManager->getToken('flux_form'))) {
            $referer = $request->headers->get('referer');

            return $this->redirect($referer);
        }
        if ($form->isSubmitted() && $form->isValid()) {
            $this->tokenManager->refreshToken('flux_form');
            $flux = $form->getData();
            try {
                if ($this->operationUtils->executeOperations($flux)) {
                    // Redirect to confirmation page
                    return $this->fluxSuccessRedirection($request, $flux);
                }
            } catch (\Exception $e) {
                $this->addFlash(
                    'error',
                    $this->translator->trans($flux->getType() . '_title', [], 'flux') . ' : ' . $e->getMessage()
                );
                $referer = $request->headers->get('referer');
                if ($referer) {
                    return $this->redirect($referer);
                }
            }
        }

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

    private function fluxSuccessRedirection(Request $request, FluxInterface $flux)
    {
        $referer = $request->headers->get('referer');
        $template = null;
        if ($this->templating->getLoader()->exists('@kohinos/flux/confirmationPage/' . $flux->getParentType() . '.html.twig')) {
            $template = '@kohinos/flux/confirmationPage/' . $flux->getParentType() . '.html.twig';
            if ($this->templating->getLoader()->exists('@kohinos/flux/confirmationPage/' . $flux->getParentType() . '-' . $flux->getType() . '.html.twig')) {
                $template = '@kohinos/flux/confirmationPage/' . $flux->getParentType() . '-' . $flux->getType() . '.html.twig';
            }
        }
        // Redirect to previous page in no confirmation page exist
        if (null === $template && $referer) {
            $this->addFlash(
                'success',
                $this->translator->trans($flux->getType() . '_title', [], 'flux') . ' : ' . $this->translator->trans('operation_success', [], 'flux')
            );

            return $this->redirect($referer);
        }

        return $this->render($template, [
            'flux' => $flux,
        ]);
    }

    /**
     * Export all operations for a user role.
     *
     * @param Request $request Request
     * @param string  $format  Format of export ('json', 'xml', 'csv', 'xls')
     * @Route("/operation/export/{format}/{currency}", name="exportUserOperation", defaults={"format": "csv", "currency" : "emlc"})
     */
    public function exportUserOperationAction(Request $request, $format = 'csv', string $currency = CurrencyEnum::CURRENCY_EMLC)
    {
        $query = $this->operationUtils->getUserOperationsByCurrency($request, $currency);

        if (null != $query) {
            // Prepare the data source
            $fields = [
                'Date' => 'createdAt',
                'Expediteur' => 'flux.expediteur',
                'Destinataire' => 'flux.destinataire',
                'Montant' => 'montant',
                'Type parent' => 'flux.parenttype',
                'Type' => 'flux.type',
                'Moyen' => 'flux.moyen',
                'Référence' => 'flux.reference',
            ];
            $source = new DoctrineORMQuerySourceIterator($query, $fields);

            $filename = sprintf(
                'export_operations_%s.%s',
                date('Y_m_d_H_i_s', strtotime('now')),
                $format
            );

            foreach ($source as $data) {
                $data['Date'] = (new \Datetime($data['Date']))->format('d/m/Y H:i:s');
                $data['Type'] = $this->translator->trans($data['Type'], [], 'flux');
                $data['Montant'] = number_format($data['Montant'], 2, '.', '');
            }

            return $this->getResponse(
                $format,
                $filename,
                $source
            );
        } else {
            $this->addFlash(
                'error',
                $this->translator->trans('Export des opérations impossible.')
            );

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

    /**
     * Export all transferts / transactions for a user role.
     *
     * @param Request $request Request
     * @param string  $format  Format of export ('json', 'xml', 'csv', 'xls')
     * @Route("/flux/export/{format}/", name="exportUserFlux", defaults={"format": "csv"})
     */
    public function exportFluxAction(Request $request, $format = 'csv')
    {
        //Prepare query depending on user role
        $query = null;
        $user = $this->security->getUser();
        if (null != $this->session->get('_prestagere') && $user->isGranted('ROLE_PRESTATAIRE')) {
            $query = $this->em->getRepository(Flux::class)->getQueryByPrestataire($this->session->get('_prestagere'));
        } elseif (null != $user->getAdherent() && $user->isGranted('ROLE_ADHERENT')) {
            $query = $this->em->getRepository(Flux::class)->getQueryByAdherent($user->getAdherent());
        } elseif (null != $this->session->get('_comptoirgere') && $user->isGranted('ROLE_COMPTOIR')) {
            $query = $this->em->getRepository(Flux::class)->getQueryByComptoir($this->session->get('_comptoirgere'));
        } elseif (null != $this->session->get('_groupegere') && $user->isGranted('ROLE_GESTION_GROUPE')) {
            $query = $this->em->getRepository(Flux::class)->getQueryByGroupe($this->session->get('_groupegere'));
        }

        if (null != $query) {
            // Prepare the data source
            $fields = [
                'Date' => 'createdAt',
                'Expediteur' => 'expediteur',
                'Destinataire' => 'destinataire',
                'Type' => 'type',
                'Montant' => 'montant',
                'Moyen' => 'moyen',
                'Opérateur' => 'operateur',
                'Référence' => 'reference',
            ];
            $source = new DoctrineORMQuerySourceIterator($query, $fields);

            $filename = sprintf(
                'export_flux_%s.%s',
                date('Y_m_d_H_i_s', strtotime('now')),
                $format
            );

            foreach ($source as $data) {
                $data['Date'] = (new \Datetime($data['Date']))->format('d/m/Y H:i:s');
                $data['Type'] = $this->translator->trans($data['Type'], [], 'flux');
                $data['Montant'] = number_format($data['Montant'], 2, '.', '');
            }

            return $this->getResponse(
                $format,
                $filename,
                $source
            );
        } else {
            $this->addFlash(
                'error',
                $this->translator->trans('Export impossible.')
            );

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

    private function getResponse($format, $filename, $source)
    {
        switch ($format) {
            case 'xls':
                $writer = new XlsWriter('php://output');
                $contentType = 'application/vnd.ms-excel';

                break;
            case 'xml':
                $writer = new XmlWriter('php://output');
                $contentType = 'text/xml';

                break;
            case 'json':
                $writer = new JsonWriter('php://output');
                $contentType = 'application/json';

                break;
            case 'csv':
                $writer = new CsvWriter('php://output', ',', '"', '\\', true, true);
                $contentType = 'text/csv';

                break;
            default:
                throw new \RuntimeException('Invalid format');
        }

        $callback = static function () use ($source, $writer) {
            $handler = Handler::create($source, $writer);
            $handler->export();
        };

        return new StreamedResponse($callback, 200, [
            'Content-Type' => $contentType,
            'Content-Disposition' => sprintf('attachment; filename="%s"', $filename),
        ]);
    }

    /**
     * Crée une instance de Payment et redirige vers la page de paiement.
     */
    public function preparePaymentAction(Form $form, $type, $extra_data = null)
    {
        // Enregistre les données du Flux en json, pour l'enregistrer une fois le paiement validé
        $encoders = [new JsonEncoder()];
        $normalizers = [new UuidNormalizer(), new ObjectNormalizer()];
        $serializer = new Serializer($normalizers, $encoders);

        $toSerialize = Payment::TYPE_ADHESION == $type ? $form->get('cotisation')->getData() : $form->getData();
        $data = $serializer->normalize(
            $toSerialize,
            null,
            [AbstractNormalizer::ATTRIBUTES => [
                'reference',
                'moyen',
                'montant',
                'role',
                'don' => [
                    'reference',
                    'moyen',
                    'montant',
                    'role',
                    'type',
                    'expediteur' => ['id'],
                    'destinataire' => ['id'],
                    'operateur' => ['id'],
                ],
                'expediteur' => ['id'],
                'destinataire' => ['id'],
                'operateur' => ['id'],
            ],
            ]
        );

        $jsondata = $serializer->serialize($data, 'json');

        // Prepare CB Payment
        if ('true' === $this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::USE_PAYZEN)) {
            $gatewayName = 'payzen';
        } else {
            $this->addFlash(
                'error',
                $this->translator->trans('Une erreur est survenue due à la configuration du paiement dans l\'application. Il est pour l\'instant impossible de payer par CB, merci de contacter votre monnaie locale.')
            );

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

        $storage = $this->payum->getStorage('App\Entity\Payment');

        $payment = $storage->create();
        $payment->setNumber(uniqid());
        $payment->setCurrencyCode('978');
        $payment->setDescription($type);
        $payment->setFluxData($jsondata);

        // Data to persist when payment is valid (other than Flux data)
        if (null != $extra_data) {
            $payment->setExtraData($extra_data);
        }

        if (Payment::TYPE_ADHESION == $type) {
            $payment->setTotalAmount($form->get('cotisation')->get('montant')->getData() * 100); // 1.23 EUR
            $payment->setClientId('Nouvel adhérent');
            $payment->setClientEmail($form->get('user')->get('email')->getData());
        } else {
            // $payment->setTotalAmount($form->get('montant')->getData() * 100); // 1.23 EUR
            if ($form->has('don') && $form->get('don')->getData()->getMontant() > 0) {
                $payment->setTotalAmount(($form->get('montant')->getData() * 100) + ($form->get('don')->getData()->getMontant() * 100)); // 1.23 EUR
            } else {
                $payment->setTotalAmount($form->get('montant')->getData() * 100); // 1.23 EUR
            }

            $payment->setClientId($this->getUser()->getId());
            $payment->setClientEmail($this->getUser()->getEmail());
        }

        $storage->update($payment);

        $captureToken = $this->payum->getTokenFactory()->createCaptureToken(
            $gatewayName,
            $payment,
            'payment_done' // the route to redirect after capture
        );

        return $this->redirect($captureToken->getTargetUrl());
    }

    /**
     * Fonction de traitement du paiement, à appeler :
     *  - automatiquement lorsqu'un événement se produit sur le site de Paiement (notification)
     *  - au retour sur le site par l'utilisateur.
     *
     * @Route("/payment/done/", name="payment_done")
     */
    public function doneAction(Request $request)
    {
        try {
            $token = $this->payum->getHttpRequestVerifier()->verify($request);
        } catch (\Exception  $e) {
            // Token expired
            return $this->redirectToRoute('index');
        }

        $gateway = $this->payum->getGateway($token->getGatewayName());

        // Execute Notify action
        $gateway->execute(new Notify($token));

        // Execute 'done' action according to payment status
        $gateway->execute($status = new GetHumanStatus($token));

        // Get payment
        $payment = $status->getFirstModel();

        // Check for actual payment status. If not null or new: payment has already been processed.
        if (GetHumanStatus::STATUS_CAPTURED == $payment->getStatus() || GetHumanStatus::STATUS_AUTHORIZED == $payment->getStatus()) {
            // Invalidate token
            $this->payum->getHttpRequestVerifier()->invalidate($token);

            $type = $payment->getDescription();

            if (Payment::TYPE_ACHAT_MONNAIE_ADHERENT == $type || Payment::TYPE_ACHAT_MONNAIE_PRESTA == $type) {
                $this->addFlash(
                    'success',
                    $this->translator->trans('Achat de monnaie locale numérique par Carte bancaire bien effectué !')
                );
            } elseif (Payment::TYPE_COTISATION_ADHERENT == $type || Payment::TYPE_COTISATION_PRESTA == $type) {
                $this->addFlash(
                    'success',
                    $this->translator->trans('Cotisation bien reçue. Merci !')
                );
            } elseif (Payment::TYPE_ADHESION == $type) {
                $this->addFlash(
                    'success',
                    $this->translator->trans('Votre adhésion a bien été prise en compte, bienvenue !')
                );

                // Connect new user
                return $this->guardHandler
                ->authenticateUserAndHandleSuccess(
                    $this->em->getRepository(User::class)->findOneBy(['id' => $payment->getClientId()]),
                    $request,
                    $this->authenticator,
                    'main'
                );
            }

            return $this->redirectToRoute('index');
        } elseif (GetHumanStatus::STATUS_CANCELED == $payment->getStatus() ||
                    GetHumanStatus::STATUS_EXPIRED == $payment->getStatus() ||
                    GetHumanStatus::STATUS_FAILED == $payment->getStatus()) {
            // Invalidate token
            $this->payum->getHttpRequestVerifier()->invalidate($token);

            $this->addFlash(
                'error',
                $this->translator->trans('La transaction a été annulée.')
            );

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

        // We got here, payment hasn't been processed, we're in the notification process. Update payment status and go on.
        $payment->setStatus($status->getValue());
        // @TODO : decommenter cela
        // $this->em->persist($payment);
        // $this->em->flush();

        $type = '';

        // If payment succesful, persist serialized 'Flux' stored in payment
        if (GetHumanStatus::STATUS_CAPTURED == $status->getValue() || GetHumanStatus::STATUS_AUTHORIZED == $status->getValue()) {
            $serializer = $this->container->get('serializer');
            $flux_array = json_decode($payment->getFluxData(), true);

            $type = $payment->getDescription();
            if (Payment::TYPE_ACHAT_MONNAIE_ADHERENT == $type) {
                $flux = $serializer->deserialize(
                    $payment->getFluxData(),
                    AchatMonnaieAdherent::class,
                    'json',
                    ['disable_type_enforcement' => true]
                );

                $exp = $this->em->getRepository(Siege::class)->find($flux_array['expediteur']);
                $flux->setExpediteur($exp);

                $dest = $this->em->getRepository(Adherent::class)->find($flux_array['destinataire']);
                $flux->setDestinataire($dest);

                $op = $this->em->getRepository(User::class)->find($flux_array['operateur']);
                $flux->setOperateur($op);
                $flux->setReconverti(true);
                if (null != $flux->getDon()) {
                    $flux->getDon()->setType(Don::TYPE_DON_ADHERENT);
                    $flux->getDon()->setOperateur($op);
                    $flux->getDon()->setExpediteur($dest);
                    $flux->getDon()->setDestinataire($this->em->getRepository(Prestataire::class)->findOneBy(['mlc' => true]));
                }
                $this->addFlash(
                    'success',
                    $this->translator->trans('Votre achat de monnaie locale a bien été pris en compte !')
                );
            } elseif (Payment::TYPE_ACHAT_MONNAIE_PRESTA == $type) {
                $flux = $serializer->deserialize(
                    $payment->getFluxData(),
                    AchatMonnaiePrestataire::class,
                    'json',
                    ['disable_type_enforcement' => true]
                );

                $exp = $this->em->getRepository(Siege::class)->find($flux_array['expediteur']);
                $flux->setExpediteur($exp);

                $dest = $this->em->getRepository(Prestataire::class)->find($flux_array['destinataire']);
                $flux->setDestinataire($dest);

                $op = $this->em->getRepository(User::class)->find($flux_array['operateur']);
                $flux->setOperateur($op);
                $flux->setReconverti(true);
                if (null != $flux->getDon()) {
                    $flux->getDon()->setType(Don::TYPE_DON_PRESTATAIRE);
                    $flux->getDon()->setOperateur($op);
                    $flux->getDon()->setExpediteur($dest);
                    $flux->getDon()->setDestinataire($this->em->getRepository(Prestataire::class)->findOneBy(['mlc' => true]));
                }
                $this->addFlash(
                    'success',
                    $this->translator->trans('Votre achat de monnaie locale a bien été pris en compte !')
                );
            } elseif (Payment::TYPE_COTISATION_ADHERENT == $type) {
                $flux = $serializer->deserialize(
                    $payment->getFluxData(),
                    CotisationAdherent::class,
                    'json',
                    ['disable_type_enforcement' => true]
                );

                $exp = $this->em->getRepository(Adherent::class)->find($flux_array['expediteur']);
                $flux->setExpediteur($exp);

                $dest = $this->em->getRepository(Prestataire::class)->find($flux_array['destinataire']);
                $flux->setDestinataire($dest);

                $op = $this->em->getRepository(User::class)->find($flux_array['operateur']);
                $flux->setOperateur($op);

                $flux->setRecu(true);
                if (null != $flux->getDon()) {
                    $flux->getDon()->setType(Don::TYPE_DON_ADHERENT);
                    $flux->getDon()->setOperateur($op);
                    $flux->getDon()->setExpediteur($exp);
                    $flux->getDon()->setDestinataire($this->em->getRepository(Prestataire::class)->findOneBy(['mlc' => true]));
                }
                $this->addFlash(
                    'success',
                    $this->translator->trans('Votre cotisation a bien été prise en compte !')
                );
            } elseif (Payment::TYPE_COTISATION_PRESTA == $type) {
                $flux = $serializer->deserialize(
                    $payment->getFluxData(),
                    CotisationPrestataire::class,
                    'json',
                    ['disable_type_enforcement' => true]
                );

                $exp = $this->em->getRepository(Prestataire::class)->find($flux_array['expediteur']);
                $flux->setExpediteur($exp);

                $dest = $this->em->getRepository(Prestataire::class)->find($flux_array['destinataire']);
                $flux->setDestinataire($dest);

                $op = $this->em->getRepository(User::class)->find($flux_array['operateur']);
                $flux->setOperateur($op);

                $flux->setRecu(true);

                if (null != $flux->getDon()) {
                    $flux->getDon()->setType(Don::TYPE_DON_PRESTATAIRE);
                    $flux->getDon()->setOperateur($op);
                    $flux->getDon()->setExpediteur($exp);
                    $flux->getDon()->setDestinataire($this->em->getRepository(Prestataire::class)->findOneBy(['mlc' => true]));
                }
                $this->addFlash(
                    'success',
                    $this->translator->trans('Votre cotisation a bien été prise en compte !')
                );
            } elseif (Payment::TYPE_ADHESION == $type) {
                $new_adherent_data = json_decode($payment->getExtraData());

                $adherent = new Adherent();
                $user = $this->userManager->createUser();
                $usergroup = $this->em->getRepository(Usergroup::class)->findOneByName('Adherent');
                $group = $this->em->getRepository(Groupe::class)->findOneBy(['id' => $new_adherent_data->groupe->id]);

                $user->setEmail($new_adherent_data->user->email);
                $user->setUsername($new_adherent_data->user->username);
                $user->setFirstname($new_adherent_data->user->firstname);
                $user->setLastname($new_adherent_data->user->lastname);
                $user->setPlainPassword($new_adherent_data->user->plainPassword);
                $user->setEnabled(true);
                $user->addPossiblegroup($usergroup);
                $user->addGroup($usergroup);
                $user->setAdherent($adherent);
                $adherent->setEcompte('0');
                $adherent->setUser($user);
                $adherent->setGroupe($group);

                if (null == $adherent->getGeoloc()) {
                    $geoloc = new Geoloc();
                    $geoloc->setAdresse($new_adherent_data->geoloc->adresse);
                    $geoloc->setCpostal($new_adherent_data->geoloc->cpostal);
                    $geoloc->setVille($new_adherent_data->geoloc->ville);
                    $adherent->setGeoloc($geoloc);
                }

                $this->em->persist($adherent);
                $this->em->flush();

                // Create first cotisation
                $flux = $serializer->deserialize(
                    $payment->getFluxData(),
                    CotisationAdherent::class,
                    'json',
                    ['disable_type_enforcement' => true]
                );

                $flux->setOperateur($user);
                $flux->setExpediteur($adherent);
                $flux->setDestinataire($this->em->getRepository(Prestataire::class)->findOneBy(['mlc' => true]));
                $flux->setRole('Adherent');
                $flux->setRecu(true);

                // Update payment with new user id, remove user data
                $payment->setClientId($user->getId());
                $payment->setExtraData('');
                $this->em->persist($payment);

                $this->addFlash(
                    'success',
                    $this->translator->trans('Votre adhésion a bien été prise en compte, bienvenue !')
                );
                // Connect new user
                $this->guardHandler->authenticateUserAndHandleSuccess(
                    $this->em->getRepository(User::class)->findOneBy(['id' => $payment->getClientId()]),
                    $request,
                    $this->authenticator,
                    'main'
                );
            } else {
                return new Response('', Response::HTTP_BAD_REQUEST);
            }

            $flux->setReference('CB ' . $payment->getNumber());
            $this->operationUtils->executeOperations($flux);
        } elseif (GetHumanStatus::STATUS_CANCELED == $payment->getStatus() ||
                    GetHumanStatus::STATUS_EXPIRED == $payment->getStatus() ||
                    GetHumanStatus::STATUS_FAILED == $payment->getStatus()) {
            $this->addFlash(
                'error',
                $this->translator->trans('La transaction a été annulée.')
            );
        }

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