<?php

namespace AppBundle\EventListener\Doctrine;

use AppBundle\Entity\DemandePEE;
use AppBundle\Enum\DemandePEEState;
use AppBundle\Enum\OperationPEEState;
use AppBundle\Mail\PeeMailer;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;

/**
 * @see http://doctrine-orm.readthedocs.io/en/latest/reference/events.html#entity-listeners Doctrine Entity Listeners
 * @see http://symfony.com/doc/master/bundles/DoctrineBundle/entity-listeners.html DoctrineBundle Entity Listeners
 * @author Bastien Gatellier <contact@bgatellier.fr>
 */
class DemandePEEListener
{
    /**
     * @var PeeMailer
     */
    private $mailer;

    /**
     * @var Session
     */
    private $session;

    /**
     * @var
     */
    private $statePrevious;

    /**
     * @var TranslatorInterface
     */
    private $translator;

    private $tokenStorage;

    private $user;

    /**
     * Constructor
     * @param PeeMailer $mailer
     * @param SessionInterface $session
     * @param TranslatorInterface $translator
     * @param TokenStorage $tokenStorage
     */
    public function __construct(PeeMailer $mailer, SessionInterface $session, TranslatorInterface $translator, TokenStorage $tokenStorage)
    {
        $this->mailer = $mailer;
        $this->session = $session;
        $this->translator = $translator;
        $this->tokenStorage = $tokenStorage;
    }

    /**
     * 
     * @param  DemandePEE         $application 
     * @param  PreUpdateEventArgs $event       
     */
    public function preUpdate(DemandePEE $application, PreUpdateEventArgs $event): void
    {
        if ($event->hasChangedField('state')) {
            $this->statePrevious = $event->getOldValue('state');
        }
    }

    /**
     *
     * @param  DemandePEE $application
     * @param  LifecycleEventArgs $event
     * @throws \Doctrine\ORM\NonUniqueResultException
     * @throws \Exception
     */
    public function postUpdate(DemandePEE $application, LifecycleEventArgs $event): void
    {
        // Do the additional tasks only if the state has changed and if the operation is still open
        if ($this->statePrevious && OperationPEEState::OPEN === $application->getOperationpee()->getState()) {
            $this->user = $this->tokenStorage->getToken()->getUser();

            $this->handleStateChange($application, $event->getEntityManager());
        }
    }

    /**
     * Handle additional tasks when the application state changes.
     * @param  DemandePEE $application
     * @param  EntityManagerInterface $em
     * @throws \Doctrine\ORM\NonUniqueResultException
     * @throws \Exception
     */
    private function handleStateChange(DemandePEE $application, EntityManagerInterface $em): void
    {
        $operation = $application->getOperationpee();

        switch ($application->getState()) {
            case DemandePEEState::REGISTERED:
                // Send a confirmation email to the teacher
                $whithdrawal = $this->session->get('withdrawal');
                if (is_null($whithdrawal)) $whithdrawal = false;

                $this->mailer->changeDemandeStatus($application, $this->statePrevious, $whithdrawal);

                //dump($this->user);

                // Notification si c'est l'utilisateur ou un admin
                if ($this->user->hasRole('ROLE_ADMIN_PEE') === true || $this->user->hasRole('ROLE_ADMIN_FULL') === true) {
                    $this->session->getFlashBag()->add('success', $this->translator->trans(
                        'notice.demandePEE.user.registered',
                        ['%user%' => $application->getCuser()->getFullname()],
                        'notice'
                    ));
                }

                $this->session->set('withdrawal', false);
                break;

            case DemandePEEState::PREREGISTERED:
                // Send an email to the teacher if he was waiting
                if (DemandePEEState::WAITING === $this->statePrevious) {
                    $this->mailer->changeDemandeStatus($application, $this->statePrevious);
                    
                    $this->session->getFlashBag()->add('success', $this->translator->trans(
                        'notice.demandePEE.user.preregistered',
                        ['%user%' => $application->getCuser()->getFullname()],
                        'notice'
                    ));
                }
            break;

            case DemandePEEState::WITHDRAWAL:
                // Register the first waiting teacher if:
                // - he was registered or preregistered
                // - the related operation is not already full
                if (!$operation->isFull() && in_array($this->statePrevious, [
                    DemandePEEState::REGISTERED,
                    DemandePEEState::PREREGISTERED,
                ])) {

                    // Get the first waiting application
                    $applicationWaiting = $em->getRepository(DemandePEE::class)->findWaitingFirst($operation->getId());

                    // If there is one waiting application, then change its state.
                    // This will trigger a case described above.
                    if ($applicationWaiting instanceof DemandePEE) {
                        $this->session->set('withdrawal', true);

                        $applicationWaiting->setState(DemandePEEState::REGISTERED);
                        $em->persist($applicationWaiting);
                        $em->flush();

//                        $this->session->getFlashBag()->add('success', $this->translator->trans(
//                            'notice.demandePEE.user.preregistered_automatically',
//                            ['%user%' => $applicationWaiting->getCuser()->getFullname()],
//                            'notice'
//                        ));
                    }
                }
                break;

            default:
                break;
        }
    }
}
