<?php

namespace App\EventListener;

use App\Entity\Flux;
use App\Entity\GlobalParameter;
use App\Entity\Prestataire;
use App\Entity\User;
use App\Events\FluxEvent;
use App\Events\MLCEvents;
use App\Events\PrestataireEvent;
use App\Flux\FluxInterface;
use Doctrine\ORM\EntityManagerInterface;
use FOS\UserBundle\Event\UserEvent;
use FOS\UserBundle\Model\UserManagerInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Csrf\TokenGenerator\TokenGeneratorInterface;
use Symfony\Component\Translation\TranslatorInterface;
use Twig\Environment;

/**
 * Listener responsable des évènements liés aux flux (transfert, transaction, retrait, vente...).
 */
class MLCEventListener implements EventSubscriberInterface
{
    protected $em;
    protected $mailer;
    protected $params;
    protected $templating;
    protected $translator;
    protected $router;
    protected $userManager;
    protected $tokenGenerator;

    public function __construct(EntityManagerInterface $em, \Swift_Mailer $mailer, RouterInterface $router, ParameterBagInterface $params, Environment $templating, TranslatorInterface $translator, UserManagerInterface $userManager, TokenGeneratorInterface $tokenGenerator)
    {
        $this->em = $em;
        $this->mailer = $mailer;
        $this->params = $params;
        $this->templating = $templating;
        $this->translator = $translator;
        $this->router = $router;
        $this->userManager = $userManager;
        $this->tokenGenerator = $tokenGenerator;
    }

    // public function setMailer(TwigSwiftMailer $maileri)
    // {
    // }

    public static function getSubscribedEvents()
    {
        return [
            MLCEvents::REGISTRATION_USER => 'onRegistrationUser',
            MLCEvents::REGISTRATION_ADHERENT => 'onRegistrationAdherent',
            MLCEvents::REGISTRATION_PRESTATAIRE => 'onRegistrationPrestataire',
            MLCEvents::REGISTRATION_CAISSIER => 'onRegistrationCaissier',
            MLCEvents::FLUX => 'onFlux',
            // @TODO : checker si VALIDATE_DEMANDE_ACHAT_EMLC c'est toujours d'actualité et utile ?
            MLCEvents::VALIDATE_DEMANDE_ACHAT_EMLC => 'onValidateDemandeAchatEmlc',
            MLCEvents::CHANGE_PRESTATAIRE_COMPTOIR => 'onChange',
            MLCEvents::CHANGE_ADHERENT_COMPTOIR => 'onChange',
            // MLCEvents::COTISATION_COTISATION_ADHERENT => 'onCotisation',
            // MLCEvents::COTISATION_COTISATION_PRESTATAIRE => 'onCotisation',
            // MLCEvents::TRANSACTION_ADHERENT_ADHERENT => 'onTransaction',
            // MLCEvents::TRANSACTION_ADHERENT_PRESTATAIRE => 'onTransaction',
            // MLCEvents::TRANSACTION_PRESTATAIRE_ADHERENT => 'onTransaction',
            // MLCEvents::TRANSACTION_PRESTATAIRE_PRESTATAIRE => 'onTransaction',
            // MLCEvents::TRANSFERT_COMPTOIR_GROUPE => 'onTransfert',
            // MLCEvents::TRANSFERT_GROUPE_COMPTOIR => 'onTransfert',
            // MLCEvents::TRANSFERT_GROUPE_SIEGE => 'onTransfert',
            // MLCEvents::TRANSFERT_SIEGE_GROUPE => 'onTransfert',
            // MLCEvents::TRANSFERT_PRESTATAIRE_SIEGE => 'onReconversion',
            // MLCEvents::RETRAIT_COMPTOIR_ADHERENT => 'onRetrait',
            // MLCEvents::RETRAIT_COMPTOIR_PRESTATAIRE => 'onRetrait',
            // MLCEvents::VENTE_COMPTOIR_ADHERENT => 'onVente',
            // MLCEvents::VENTE_COMPTOIR_PRESTATAIRE => 'onVente',
        ];
    }

    public function onRegistrationUser(UserEvent $event)
    {
        $user = $event->getUser();
        $this->sendMailRegistration($user, ['type' => 'user']);
    }

    public function onRegistrationPrestataire(PrestataireEvent $event)
    {
        $user = $event->getUser();
        $params = [
            'prestataire' => $event->getPrestataire(),
            'type' => $event->getType(),
        ];
        $this->sendMailRegistration($user, $params);
    }

    public function onRegistrationCaissier(PrestataireEvent $event)
    {
        $user = $event->getUser();
        $params = [
            'prestataire' => $event->getPrestataire(),
            'type' => $event->getType(),
        ];
        $this->sendMailRegistration($user, $params);
    }

    public function onRegistrationAdherent(UserEvent $event)
    {
        $user = $event->getUser();
        $this->sendMailRegistration($user, ['type' => 'adherent']);
    }

    private function sendMailRegistration(UserInterface $user, array $params)
    {
        //Set confirmation token (alike resetting password)
        if (null === $user->getConfirmationToken()) {
            $user->setConfirmationToken($this->tokenGenerator->generateToken());
        }

        // this depends on requirements
        $user->setPasswordRequestedAt(new \DateTime());
        $this->userManager->updateUser($user);

        $subject = $this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::MLC_NAME_SMALL) . ' : ' . $this->translator->trans('Création de votre compte');
        $mail = (new \Swift_Message($subject))
            ->setFrom($this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::MLC_NOTIF_EMAIL))
            ->setTo($user->getEmail())
            ->setBody(
                $this->templating->render(
                    '@kohinos/email/admin_add_' . ((isset($params['type']) && in_array($params['type'], ['adherent', 'prestataire', 'caissier', 'user'])) ? $params['type'] : 'adherent') . '.html.twig',
                    array_merge($params, [
                        'subject' => $subject,
                        'user' => $user,
                        'confirmationUrl' => $this->router->generate('fos_user_resetting_reset', ['token' => $user->getConfirmationToken()], UrlGeneratorInterface::ABSOLUTE_URL),
                    ])
                ),
                'text/html'
            );
        $this->mailer->send($mail);
    }

    public function onFlux(FluxEvent $event)
    {
        $this->sendFluxNotificationEmail($event);
    }

    public function onValidateDemandeAchatEmlc(FluxEvent $event)
    {
        $this->sendFluxNotificationEmail($event);
    }

    /**
     * Send Flux Notification Email.
     *
     * @param FluxEvent $event [description]
     */
    private function sendFluxNotificationEmail(FluxEvent $event)
    {
        $alreadySendEmail = [];
        foreach ($event->getFlux()->getUsersToNotify() as $type => $users) {
            // TRANSFORM :
            //     expediteurs => exp
            //     destinataires => des
            $type = substr($type, 0, 3);
            if ($event->getFlux()->isValidationAchat()) {
                $type .= '_validate';
            }
            foreach ($users as $user) {
                if ($user instanceof User && $user->getAlertemailflux()) {
                    $this->sendMail($user, $event->getFlux(), $type);
                } elseif ('siege' == $user) {
                    $users = $this->em->getRepository(User::class)->findByRole('ROLE_ADMIN_SIEGE');
                    foreach ($users as $userAdminSiege) {
                        if ($userAdminSiege->getAlertemailflux()) {
                            $this->sendMail($userAdminSiege, $event->getFlux(), $type);
                            $alreadySendEmail[] = $userAdminSiege->getEmail();
                        }
                    }
                } elseif ('tresorier' == $user) {
                    $users = $this->em->getRepository(User::class)->findByRole('ROLE_TRESORIER');
                    foreach ($users as $userTresorier) {
                        // On envoi une notification au(x) trésorier(s) global(aux) et au(x) trésorier(s) du groupe local lors d'une demande de reconversion !
                        if (Flux::TYPE_RECONVERSION == $event->getFlux()->getParentType()
                            && $userTresorier->getAlertemailflux()
                            && (null != $event->getFlux()->getExpediteur()->getGroupe() && $userTresorier->getGroupesgeres()->contains($event->getFlux()->getExpediteur()->getGroupe()) || 0 == count($userTresorier->getGroupesgeres()))
                            && (!in_array($userTresorier->getEmail(), $alreadySendEmail))) {
                            $alreadySendEmail[] = $userTresorier->getEmail();
                            $this->sendMail($userTresorier, $event->getFlux(), $type);
                        }
                    }
                }
            }
        }
    }

    /**
     * Send email notification for flux.
     *
     * @param User        $user     User to send notification
     * @param Flux        $flux     Flux
     * @param string      $type     expediteurs (exp) or destinataires (des)
     * @param string|null $template If different for some kind of logic (validate achat monnaie for example)
     */
    private function sendMail(User $user, FluxInterface $flux, string $type, string $template = null)
    {
        if (null == $template) {
            $template = $this->getEmailTemplate($flux, $type);
        }
        $usertype = (('exp' == $type && $flux->getExpediteur() instanceof Prestataire) || ('des' == $type && $flux->getDestinataire() instanceof Prestataire)) ? 'prestataire' : 'adhérent';

        $subject = $this->translator->trans($type . '_' . $flux->getType() . '_email_subject', [
            '%mlcshortname%' => $this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::MLC_NAME_SMALL),
            '%mlcname%' => $this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::MLC_NAME),
            '%mlcsymbol%' => $this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::MLC_SYMBOL),
            '%destinataire%' => $flux->getDestinataire()->__toString(),
            '%expediteur%' => $flux->getExpediteur()->__toString(),
            '%usertype%' => $user,
            '%type%' => $flux->getType(),
            '%reference%' => $flux->getReference(),
            '%moyen%' => $flux->getMoyen(),
            '%montant%' => $flux->getMontant(),
        ], 'flux');

        $mail = (new \Swift_Message($subject))
            ->setFrom($this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::MLC_NOTIF_EMAIL))
            ->setTo($user->getEmail())
            ->setBody(
                $this->templating->render(
                    $template,
                    [
                        'subject' => $subject,
                        'user' => $user,
                        'flux' => $flux,
                    ]
                ),
                'text/html'
            );
        $this->mailer->send($mail);
    }

    /**
     * Get email template from flux if exist.
     *
     * @param FluxInterface $flux Flux
     * @param string        $type expediteurs or destinataires
     *
     * @return string
     */
    private function getEmailTemplate(FluxInterface $flux, string $type)
    {
        // Un template d'email différent en fonction du parentytpe ou/et du type de flux et en fonction de la "fonction" de l'utilisateur expediteur/destinataire
        $template = '@kohinos/email/notification_flux.html.twig';
        if ($this->templating->getLoader()->exists('@kohinos/email/flux/' . $type . '_' . $flux->getParentType() . '.html.twig')) {
            $template = '@kohinos/email/flux/' . $type . '_' . $flux->getParentType() . '.html.twig';
        }
        if ($this->templating->getLoader()->exists('@kohinos/email/flux/' . $type . '_' . $flux->getType() . '.html.twig')) {
            $template = '@kohinos/email/flux/' . $type . '_' . $flux->getType() . '.html.twig';
        }

        return $template;
    }

    // @TODO => notifications/emails differents suivant le type d'action
    // public function onCotisation(FluxEvent $event)
    // {
    // }

    // public function onTransaction(FluxEvent $event)
    // {
    // }

    // public function onTransfert(FluxEvent $event)
    // {
    // }

    // public function onChange(FluxEvent $event)
    // {
    // }

    // public function onReconversion(FluxEvent $event)
    // {
    // }

    // public function onRetrait(FluxEvent $event)
    // {
    // }

    // public function onVente(FluxEvent $event)
    // {
    // }
}
