<?php

namespace AppBundle\Controller\Admin;

use AppBundle\Entity\DemandePEE;
use AppBundle\Entity\OperationPEE;
use AppBundle\Entity\SMSCampaignPEE;
use AppBundle\Entity\User;
use AppBundle\Enum\DemandePEEState;
use AppBundle\Enum\UserType;
use AppBundle\Export\OperationPeeExporter;
use AppBundle\Form\Type\PEE\OperationPEEType;
use AppBundle\Pdf\PdfGenerator;
use AppBundle\Helper\UserHelper;
use AppBundle\Translation\ConstTranslator;
use Cocur\Slugify\Slugify;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
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\RouterInterface;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\JsonResponse;

class OperationPEEController extends Controller
{
    /**
     * @var EntityManagerInterface
     */
    private $em;

    /**
     * @var OperationPeeExporter
     */
    private $operationPeeExporter;

    /**
     * @var PdfGenerator
     */
    private $pdfGenerator;

    /**
     * @var RouterInterface
     */
    private $router;

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

    /**
     * @var ConstTranslator
     */
    private $utils;
    
    public function __construct(
        EntityManagerInterface $em,
        OperationPeeExporter $operationPeeExporter,
        PdfGenerator $pdfGenerator,
        RouterInterface $router,
        SessionInterface $session,
        ConstTranslator $utils
    )
    {
        $this->em = $em;
        $this->operationPeeExporter = $operationPeeExporter;
        $this->pdfGenerator = $pdfGenerator;
        $this->router = $router;
        $this->session = $session;
        $this->utils = $utils;
    }

    /**
     * Liste de l'ensemble des opérations PEE.
     *
     * @Route(
     *      "/operation-pee/list/{page}",
     *      requirements = {"page": "\d+"},
     *      name = "admin_operation_pee"
     * )
     *
     * @param Request $request
     * @param int $page
     * @return Response
     */
    public function indexAction(Request $request, int $page = 1)
    {
        $resultsMaxPerPage = $this->getParameter('results.admin.max_per_page');

        // Variables de recherche
        if (null !== $date = $request->query->get('date')) {
            $date = \DateTime::createFromFormat('d/m/Y', $date);
        }
        $academy = $request->query->get('academy');
        $state = $request->query->get('state');
        $company = $request->query->get('company');

        // Variables de tri
        $sortby = $request->get('sortby');
        $sortdirection = $request->get('sortdirection');

        // Mise à jour de la session avec les nouvelles valeurs
        $this->session->set($request->get('_route'), array(
            'page' => $page,
            'date' => $date,
            'academy' => $academy,
            'state' => $state,
            'company' => $company,
            'sortby' => $sortby,
            'sortdirection' => $sortdirection,
        ));

        // Récupération des valeurs de tri, et affectation de valeurs correctes aux variables.
        // Cette étape d'affectation est une sécurité permettant de ne pas afficher directement le nom des champs de BDD dans l'interface (via les URL).
        switch ($sortby) {
            case 'entreprise':
                $sortbyBDD = 'company';
                break;

            default:
                $sortby = 'date';
                $sortbyBDD = 'created';
                break;
        }

        if ('desc' !== $sortdirection && 'asc' !== $sortdirection) {
            $sortdirection = 'desc';
        }

        // Récupération de la liste des académies
        $academies = $this->em->getRepository(\AppBundle\Entity\User::class)->findAcademies(Query::HYDRATE_ARRAY);

        // Récupèration de la liste des états possibles d'une opération PEE
        $states = $this->utils->trans(\AppBundle\Enum\OperationPEEState::class);

        // Récupération de la liste des opérations PEE
        $qb = $this->em->createQueryBuilder();
        $qb
            ->select('o')
            ->from(OperationPEE::class, 'o')
            ->orderBy('o.'.$sortbyBDD, $sortdirection)
            ->addOrderBy('o.id', 'desc')
            ->setFirstResult(($page - 1) * $resultsMaxPerPage)
            ->setMaxResults($resultsMaxPerPage);
        if ($state) {
            $qb
                ->where('o.state = :o_state')
                ->setParameter('o_state', $state);
        }
        if ($academy) {
            $qb
                ->andWhere($qb->expr()->like(
                    $qb->expr()->upper('o.academy'),
                    $qb->expr()->upper(':o_academy')
                ))
                ->setParameter('o_academy', '%'.$academy.'%');
        }
        if ($company) {
            $qb
                ->leftJoin('o.company', 'c')
                ->andWhere($qb->expr()->orX(
                    $qb->expr()->like(
                        $qb->expr()->upper('o.site'),
                        $qb->expr()->upper(':o_site')
                    ),
                    $qb->expr()->like(
                        $qb->expr()->upper('c.name'),
                        $qb->expr()->upper(':c_name')
                    )
                ))
                // Ne fonctionne pas avec setParameters([]). Bizarre...
                ->setParameter('o_site', '%'.$company.'%')
                ->setParameter('c_name', '%'.$company.'%');
        }
        if ($date) {
            $qb
                ->andWhere('o.dateVisite = :o_dateVisite')
                ->setParameter('o_dateVisite', $date);
        }
        $query = $qb->getQuery();

        $operations = new \Doctrine\ORM\Tools\Pagination\Paginator($query);

        // Récupération du nombre d'inscrits, de pré-inscrits et d'en attente pour toutes les opérations, et crée un tableau les regroupant.
        // Le tableau ainsi créé contient autant d'éléments que celui des opérations, et les index correspondent.
        // Par exemple, $inscrits[5] contient les informations relatives au nombre d'inscrits de l'opération contenue dans $operations[5].
        // Cette "synchronisation" simplifie la récupération des informations dans la vue (template) car les informations sont récupérées dans le controller
        // (elles étaient précédemment récupérées dans la vue, ce qui impliquait d'appeler pour chacun des trois états une boucle de rendue,
        // ayant pour conséquence de dégrader fortement l'a consommation mémoire). Ce changement a ainsi permis de diminuer la consommation mémoire de 85% (~200mo à seulement ~30mo)
        $inscrits = [];
        foreach ($operations as $ope_key => $ope) {
            $qb = $this->em->createQueryBuilder();
            $qb->select('d.state, COUNT(d.state) AS nb')
                ->from(DemandePEE::class, 'd')
                ->where('d.operationpee = :d_operationpee')
                ->andWhere($qb->expr()->in('d.state', ':dState'))
                ->groupBy('d.state')
                ->setParameters([
                    'd_operationpee' => $ope->getId(),
                    'dState' => [
                        DemandePEEState::REGISTERED,
                        DemandePEEState::PREREGISTERED,
                        DemandePEEState::WAITING,
                    ],
                ]);
            $subscribers = $qb->getQuery()->getArrayResult();

            $inscrits[$ope_key] = [];
            foreach ($subscribers as $subscriber) {
                $inscrits[$ope_key][$subscriber['state']] = $subscriber['nb'];
            }
        }

        return $this->render('Admin/Ope/PEE/indexOperation.html.twig', array(
            'operations' => $operations,
            'inscrits' => $inscrits,
            'nbPages' => ceil(count($operations) / $resultsMaxPerPage),
            'pageactuelle' => $page,
            'date' => $date,
            'company' => $company,
            'states' => $states,    // Tableau de tous les états
            'state_selected' => $state, // État actuellement sélectionné
            'academies' => $academies,
            'academy_selected' => $academy,
            'sortby' => $sortby,
            'sortdirection' => $sortdirection,
        ));
    }

    /**
     * Ajout d'une opération PEE
     *
     * @Route(
     *      "/operation-pee/add",
     *      name = "admin_operation_pee_new"
     * )
     *
     * @param Request $request
     * @return \Symfony\Component\HttpFoundation\RedirectResponse|Response
     */
    public function addAction(Request $request)
    {
        $ope = new OperationPEE;

        $form = $this->createForm(OperationPEEType::class, $ope);
        $form->handleRequest($request);

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

            // On définit un message flash
            $this->addFlash('success', 'Opération ajoutée');

            return $this->redirectToRoute('admin_operation_pee_edit', [
                'id' => $ope->getId(),
            ]);
        }

        return $this->render('Admin/Ope/PEE/addOperation.html.twig', [
            'form' => $form->createView(),
        ]);
    }

    /**
     * Edit / duplicate an PEE operation
     *
     * @Route(
     *      "/operation-pee/{id}/edit",
     *      name = "admin_operation_pee_edit"
     * )
     * @Route(
     *      "/operation-pee/{id}/duplicate",
     *      name = "admin_operation_pee_duplicate"
     * )
     *
     * @param Request $request
     * @param OperationPEE $ope
     * @return \Symfony\Component\HttpFoundation\RedirectResponse|Response
     * @throws \ReflectionException
     */
    public function editAction(Request $request, OperationPEE $ope)
    {
        $applications = [];
        $editMode = $this->router->matchRequest($request)['_route'] === 'admin_operation_pee_edit';

        $form = $this->createForm(OperationPEEType::class, $ope);
        $form->handleRequest($request);

        if ($editMode) {
            $allowedStates = DemandePEEState::getConstants();
            $userApplications = $this->em->getRepository(DemandePEE::class)->findByOperationAndStates($ope->getId(), $allowedStates);

            foreach ($allowedStates as $state) {
                $applications[$state] = [
                    'trans' => $this->utils->trans(DemandePEEState::class, $state)[$state],
                    'list' => array_filter($userApplications, function($a) use ($state) {
                        return $state === $a['state'];
                    })
                ];
            }
        }

        if ($form->isSubmitted() && $form->isValid()) {
            $flashBagMsg = 'Opération PEE ';

            if ($editMode) {
                $flashBagMsg .= 'modifiée';
            } else { // Duplicate mode
                $this->em->detach($ope); // Avoid current operation update
                
                $ope = clone $ope;

                $flashBagMsg .= 'dupliquée';
            }

            if ($request->get('remove_document')) {
                $ope->setDescription(null);
            }

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

            $this->addFlash('success', $flashBagMsg);

            return $this->redirectToRoute('admin_operation_pee_edit', [
                'id' => $ope->getId(),
            ]);
        }
        
        $sms_campaigns = $this->em->getRepository(SMSCampaignPEE::class)->findByOperationpee($ope->getId(), ['created' => 'DESC']);

        return $this->render('Admin/Ope/PEE/editOperation.html.twig', [
            'applications' => $applications,
            'edit_mode' => $editMode,
            'form' => $form->createView(),
            'ope' => $ope,
            'sms_campaigns' => $sms_campaigns,
        ]);
    }

    /**
     * Supprime une opération PEE
     *
     * @Route(
     *      "/operation-pee/{operationpee}/delete",
     *      name = "admin_operation_pee_delete"
     * )
     *
     * @param Request $request
     * @param OperationPEE $operationpee
     * @return \Symfony\Component\HttpFoundation\RedirectResponse|Response
     */
    public function deleteAction(Request $request, OperationPEE $operationpee)
    {
        $this->em->remove($operationpee);
        $this->em->flush();

        $this->addFlash('success', 'Elément supprimé');

        return $request->isXmlHttpRequest()
            ? new Response()
            : $this->redirectToRoute('admin_operation_pee')
        ;
    }

    /**
     * Export the list of users of an operation,
     * according to the state of their application.
     * @Route(
     *      "/operation-pee/{operationPEE}/export/{state}",
     *      name = "admin_operation_pee_export"
     * )
     * @param OperationPEE $operationPEE
     * @param string $state
     * @return \Symfony\Component\HttpFoundation\StreamedResponse Excel file
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
     * @throws \ReflectionException
     */
    public function exportAction(OperationPEE $operationPEE, string $state): StreamedResponse
    {
        $slugify = new Slugify();
        $filename = join(' - ', [
            $slugify->slugify($state),
            $slugify->slugify($operationPEE->getCompany()),
            $slugify->slugify($operationPEE->getSite()),
            date('d-m-Y')
        ]);

        // Retrieve the applications from the DB.
        $applications = $this->em->getRepository(DemandePEE::class)->findByOperationAndStates($operationPEE->getId(), [$state]);

        return $this->operationPeeExporter->exportApplications($applications, $filename.".xlsx");
    }

    /**
     * Generate a printable version of a PEE operation.
     * Default is PDF file, but an HTML version can be procedeed with the addition of .html in the url.
     *
     * @Route(
     *      "/operation-pee/{id}/download.{_format}",
     *      name = "admin_operation_pee_download",
     *      requirements = {"_format": "html|pdf"},
     *      defaults = {"_format": "pdf"}
     * )
     *
     * @param Request $request
     * @param OperationPEE $o
     * @return Response
     */
    public function downloadAction(Request $request, OperationPEE $o): Response
    {
        return $this->pdfGenerator->generateOperationPee($o, $request->getRequestFormat());
    }


    /**
     * Export pièces d'identité
     *
     * @Route(
     *      "/operation-pee/{id}/export-pieces",
     *      name = "admin_operation_pee_export_pieces"
     * )
     *
     * @param Request $request
     * @param OperationPEE $ope
     * @return \Symfony\Component\HttpFoundation\RedirectResponse|Response
     * @throws \ReflectionException
     */
    public function exportPiecesAction(Request $request, OperationPEE $ope)
    {
            $qb = $this->em->createQueryBuilder();
            $qb->select("d, u")
                ->from(DemandePEE::class, 'd')
                ->join('d.cuser', 'u')
                ->where('d.operationpee = :d_operationpee')
                ->andWhere('d.state = :dState')
                ->setParameters([
                    'd_operationpee' => $ope->getId(),
                    'dState' => DemandePEEState::REGISTERED
                ]);
            $subscribers = $qb->getQuery()->getResult();

        if (!empty($subscribers)) {
            try {
                $zip = new \ZipArchive;
                $zipName = 'pieces-'.$ope->getId().'.zip';
                $zipEmpty = true;

                    if ($zip->open($zipName, \ZipArchive::CREATE) === TRUE) {

                        foreach ($subscribers as $d) {
                            if (is_null($d->getCuser()->getIdentityCard2()) === false && file_exists($d->getCuser()->getAbsoluteIdentityCard1())) {
                                $zipEmpty = false;
                                $zip->addFile($d->getCuser()->getAbsoluteIdentityCard1(), $d->getCuser()->getIdentityCard1());
                            }

                            if (is_null($d->getCuser()->getIdentityCard2()) === false && file_exists($d->getCuser()->getAbsoluteIdentityCard1())) {
                                $zipEmpty = false;
                                $zip->addFile($d->getCuser()->getAbsoluteIdentityCard2(), $d->getCuser()->getIdentityCard2());
                            }
                        }

                        $zip->close();

                        if ($zipEmpty === false) {
                            header("Pragma: public");
                            header("Expires: 0");
                            header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
                            header("Cache-Control: private", false);
                            header("Content-Type: application/zip");
                            header("Content-Disposition: attachment; filename=" . basename($zipName) . ";");
                            header("Content-Transfer-Encoding: binary");
                            header("Content-Length: " . filesize($zipName));
                            readfile($zipName);
                            unlink($zipName);
                            exit;
                        } else {
                            $this->addFlash('error', 'Aucune pièce d\'identité trouvée');

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

            } catch(\Exception $e) {
                echo $e->getMessage();
                die();
            }
        }
    }


    /**
     * Edit / duplicate an PEE operation
     *
     * @Route(
     *      "/operation-pee/{id}/participants/add",
     *      name = "admin_operation_pee_add_participant"
     * )
     *
     * @param Request $request
     * @param OperationPEE $ope
     * @return \Symfony\Component\HttpFoundation\RedirectResponse|Response
     * @throws \ReflectionException
     */
    public function addParticipantAction(Request $request, OperationPEE $ope)
    {
        $user = null;
        $data = null;

        $userManager = $this->container->get('fos_user.user_manager');

        /**
         * Create a new teacher
         */
        $formCreate = $this->createFormBuilder(null, ['translation_domain' => 'form'])
            ->add('lastname', TextType::class, [
                'label' => 'form.profile.lastname.label',
                'required' => true
            ])
            ->add('firstname', TextType::class,[
                'label' => 'form.profile.firstname.label',
                'required' => true
            ])
            ->add('email', EmailType::class, [
                'label' => 'form.email.label',
                'required' => true
            ])
            ->add('phone', TextType::class, [
                'label' => 'form.profile.phone.label',
                'required' => false
            ])
            ->getForm();

        $formCreate->handleRequest($request);

        if ($formCreate->isSubmitted() && $formCreate->isValid()) {
            $data = $formCreate->getData();

            // Test doublon
            $user = $userManager->findUserBy(array('email' => $data['email']));

            if (is_null($user)) {
                // Ajout du professeur si pas d'identifiant renseigné
                $user = $userManager->createUser();

                $user->setLastname($data['lastname']);
                $user->setFirstname($data['firstname']);
                $user->setEmail($data['email']);
                $user->setPhone($data['phone']);
                $user->setType(UserType::TEACHER);

                // Champs obligatoires
                UserHelper::setUserManager($userManager);
                $user->setUsername(UserHelper::generateUsername($user));
                $user->setEnabled(1);

                $tokenGenerator = $this->container->get('fos_user.util.token_generator');
                $password = substr($tokenGenerator->generateToken(), 0, 8); // 8 chars

                $user->setPlainPassword($password);

                $userManager->updateUser($user);


                if (is_null($user)) {
                    $this->addFlash('error', 'Professeur non créé');
                }

                $this->em->persist($user);
            } else {
                $this->addFlash('error', "Il existe un compte avec cette adresse email");
                $user = null; // Permet de ne pas enregistrer la demande
            }
        }

        /**
         * Add teacher
         */
        $formAdd = $this->createFormBuilder(null, ['translation_domain' => 'form'])
            ->add('search', TextType::class, [
                'required' => true
            ])->add('id', HiddenType::class, [
                'required' => true
            ])
            ->getForm();

        $formAdd->handleRequest($request);

        if ($formAdd->isSubmitted() && $formAdd->isValid()) {
            $data = $formAdd->getData();
            if (!empty($data['id'])) {
                $user = $userManager->findUserBy(array('id' =>$data['id']));

                if (is_null($user)) {
                    $this->addFlash('error', 'Professeur non trouvé');
                }
            }
        }

        // Si un des formulaire est valide et qu'on a un utilisateur on créé la demande
        if (!empty($data) && !is_null($user)) {
            
            $demande = new DemandePEE;
            $demande->setOperationpee($ope);
            $demande->setCuser($user);

            $demande->setState(DemandePEEState::REGISTERED);

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

            $this->addFlash('success', 'Professeur ajouté à l\'opération');
            
            return $this->redirectToRoute('admin_operation_pee_edit', [
                'id' => $ope->getId(),
            ]);
        }
        
        return $this->render('Admin/Ope/PEE/addParticipant.html.twig', [
            'form' => $formCreate->createView(),
            'formAdd' => $formAdd->createView(),
            'ope' => $ope
        ]);
    }

    /**
     * Recherche user autocomplete
     *
     * @Route(
     *      "/operation-pee/teacher/search",
     *      name = "admin_operation_pee_search_teacher"
     * )
     *
     * @param Request $request
     * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
     */
    public function searchTeacherAjaxAction(Request $request)
    {
        $users = array();
        $search = $request->query->get('term');

        if (!empty($search)) {
            $search = "%" . trim($search) . "%";
            $users = $this->em->getRepository(User::class)->searchUsers($search);

            //dump($users);
        }


        return new JsonResponse($users);
    }
}
