<?php

/*
 * kohinos_cooperatic
 * Copyright (C) 2019-2020  ADML63
 * Copyright (C) 2020- Cooperatic
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
namespace App\Controller;

use App\Events\MLCEvents;
use App\Events\FluxEvent;
use Doctrine\ORM\EntityManagerInterface;
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\StreamedResponse;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Translation\TranslatorInterface;
use App\Entity\Flux;
use App\Entity\User;
use App\Entity\Adherent;
use App\Entity\Prestataire;
use App\Entity\Groupe;
use Sonata\Exporter\Handler;
use Sonata\Exporter\Source\DoctrineORMQuerySourceIterator;
use Sonata\Exporter\Writer\CsvWriter;
use Sonata\Exporter\Writer\JsonWriter;
use Sonata\Exporter\Writer\XmlWriter;
use Sonata\Exporter\Writer\XlsWriter;

/**
 *
 * 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)
 *
 */
class FluxController extends AbstractController
{
    protected $em;
    protected $translator;
    protected $eventDispatcher;
    protected $session;

    public function __construct(Security $security,
                                EntityManagerInterface $em,
                                TranslatorInterface $translator,
                                EventDispatcherInterface $eventDispatcher,
                                SessionInterface $session)
    {
        $this->security = $security;
        $this->em = $em;
        $this->translator = $translator;
        $this->eventDispatcher = $eventDispatcher;
        $this->session = $session;
    }

    protected function manageFluxForm(Request $request, Form $form, $compte, $success, $title)
    {
        if ($this->security->getUser() == null) {
            throw new \Exception("[FLUX] Opération impossible ! Utilisateur déconnecté !");
        }
        $form->handleRequest($request);
        if ($form->isSubmitted() && $form->isValid()) {
            $data = $form->getData();
            $this->em->persist($data);
            $this->em->flush();
            $this->addFlash(
                'success',
                $success
            );
            $this->eventDispatcher->dispatch(
                MLCEvents::FLUX,
                new FluxEvent($data)
            );
            $referer = $request->headers->get('referer');
            if ($referer && !$request->isXmlHttpRequest()) {
                return $this->redirect($referer);
            } elseif (!$request->isXmlHttpRequest()) {
                return new Response('', Response::HTTP_BAD_REQUEST);
            }
        }

        return $this->render('flux/transaction.html.twig', [
            'form' => $form->createView(),
            'title' => $title
        ]);
    }

    /**
     * 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 ($this->session->get('_prestagere') != null) {
          if ($this->security->isGranted('ROLE_PRESTATAIRE')) {
            $query = $this->em->getRepository(Flux::class)->getQueryByPrestataire($this->session->get('_prestagere'));
          } elseif ($this->security->isGranted('ROLE_CAISSIER_PRESTATAIRE')) {
            $query = $this->em->getRepository(Flux::class)->getQueryByCaissierprestataire($this->session->get('_prestagere'));
          }
        } elseif ($user->getAdherent() != null && $this->security->getUser()->isGranted('ROLE_ADHERENT')) {
          $query = $this->em->getRepository(Flux::class)->getQueryByAdherent($user->getAdherent());
        } elseif ($this->session->get('_comptoirgere') != null && ($this->security->isGranted('ROLE_COMPTOIR') || $this->security->isGranted('ROLE_CAISSIER_COMPTOIR'))) {
          $query = $this->em->getRepository(Flux::class)->getQueryByComptoir($this->session->get('_comptoirgere'));
        } elseif ($this->session->get('_groupegere') != null && $this->security->getUser()->isGranted('ROLE_GESTION_GROUPE')) {
          $query = $this->em->getRepository(Flux::class)->getQueryByGroupe($this->session->get('_groupegere'));
        }

        if ($query != null) {
          // Prepare the data source
          $fields = ['created_at', 'expediteur', 'destinataire', 'type', 'parenttype', 'montant', 'moyen', 'operateur'];
          $source = new DoctrineORMQuerySourceIterator($query, $fields);

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

          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),
        ]);
    }
}
