<?php

declare(strict_types=1);

namespace App\Command;

use App\Entity\Flux;
use App\Entity\GlobalParameter;
use App\Entity\Prestataire;
use App\Entity\User;
use App\Utils\CustomEntityManager;
use App\Utils\OperationUtils;
use IntlDateFormatter;
use NumberFormatter;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\HttpKernel\KernelInterface;
use Twig\Environment;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Error\SyntaxError;

/**
 * This command is part of the CCAS reconversion process.
 * It is used in conjonction with ReconversionCcasMonaPrestataires.
 * It has been separated from ReconversionCcasMonaPrestataires
 * so that we can replay it in case of mailing issue
 * without performing the "Demande de reconversion" again.
 *
 * @SuppressWarnings(PHPMD.CyclomaticComplexity)
 */
class SendCcasTransactionsExportToPrestatairesCommand extends Command
{

    protected static $defaultName = 'kohinos:ssa:export-ccas-transactions';

    protected $em;
    protected $mailer;
    protected $templating;
    protected $io;
    protected $param;
    protected $operationUtils;

    public function __construct(
        CustomEntityManager $em,
        \Swift_Mailer $mailer,
        Environment $templating,
        OperationUtils $operationUtils
    ) {
        $this->em = $em;
        $this->mailer = $mailer;
        $this->templating = $templating;
        $this->operationUtils = $operationUtils;
        parent::__construct();
    }

    /**
     * @param Prestataire $p
     * @param $monthYearStr
     * @param $montant
     * @param $path
     *
     * @throws LoaderError
     * @throws RuntimeError
     * @throws SyntaxError
     */
    public function prepareMail(Prestataire $p, $monthYearStr, $montant): \Swift_Message
    {
        $this->io->text('Envoi du mail pour le prestataire ' . $p->getRaison());
        $subject = "Expérimentation de Sécurité Sociale de l’Alimentation en Gironde – Facture CCAS $monthYearStr";
        $globalParamRepo = $this->em->getRepository(GlobalParameter::class);
        //Send to prestataire
        //Send copy to tresoriers + gestionnaire de groupe (or default contact if linked gestionnaire de groupe does not exists)
        $copyTo = array();
        $users = $this->em->getRepository(User::class)->findByRole('ROLE_TRESORIER');
        foreach ($users as $userTresorier) {
            $copyTo[] = $userTresorier->getEmail();
        }
        foreach ($globalParamRepo->getMailOfGestionnaireDeGroupeAndDefaultContact($p) as $copyToEmail) {
            $copyTo[] = $copyToEmail;
        }
        $mail = (new \Swift_Message($subject))
            ->setFrom($globalParamRepo->val(GlobalParameter::MLC_NOTIF_EMAIL))
            ->setTo($p->getGestionnairesEmailsArray())
            ->setCc($copyTo)
            ->setBody(
                $this->templating->render(
                    '@kohinos/email/tav/ccas_transactions.html.twig',
                    [
                        'subject' => "Facture CCAS $monthYearStr",
                        'montant' => $montant,
                        'monthyear' => $monthYearStr
                    ]
                ),
                'text/html'
            );
        return $mail;
    }

    private function fputcsvSeparatedBySemicolon($file, $arr)
    {
        fputcsv($file, $arr, ";");
    }

    /**
     * @param $year
     * @param int         $month
     * @param Prestataire $p
     * @param string      $dir
     * @param $data
     * @param NumberFormatter $nf
     *
     * @return array
     */
    public function createExportFile($year, int $month, Prestataire $p, string $dir, $data, NumberFormatter $nf): array
    {
        $filename = sprintf(
            'ssagir_ccastrans_%s_%s.%s',
            $year . '-' . $month,
            $p->getSlug(),
            'csv'
        );
        $path = $dir . '/' . $filename;

        $file = fopen($path, 'w');

        //Write header
        $this->fputcsvSeparatedBySemicolon($file, [
            "Numero d'anonymisation",
            'Montant (€)',
            'Montant en lettres',
            'Date',
        ]);

        //Init data used in loop to write intermediate totals
        $prestaTotal = 0;
        $clientTotal = 0;
        $previousAnonymousToken = $data[0]['anonymous_token'];

        //Write body
        foreach ($data as $row) {
            //Write intermediate total line before moving to next client
            if ($previousAnonymousToken !== $row['anonymous_token']) {
                $this->fputcsvSeparatedBySemicolon($file, [
                    'TOTAL ' . $previousAnonymousToken,
                    number_format($clientTotal, 2),
                    $this->currencySpellout($nf, $clientTotal),
                    '',
                ]);
                $clientTotal = 0;
                $previousAnonymousToken = $row['anonymous_token'];
            }
            //Write transaction line
            $this->fputcsvSeparatedBySemicolon($file, [
                $row['anonymous_token'],
                number_format(floatval($row['montant']), 2),
                $this->currencySpellout($nf, floatval($row['montant'])),
                $row['created_at'],
            ]);
            $clientTotal += $row['montant'];
            $prestaTotal += $row['montant'];
        }
        //Write last intermediate total line
        $this->fputcsvSeparatedBySemicolon($file, [
            'TOTAL ' . $row['anonymous_token'],
            number_format($clientTotal, 2),
            $this->currencySpellout($nf, $clientTotal),
            '',
        ]);
        //Write final total line
        $this->fputcsvSeparatedBySemicolon($file, [
            'TOTAL ' . $p->getRaison(),
            number_format($prestaTotal, 2),
            $this->currencySpellout($nf, $prestaTotal),
            '',
        ]);

        fclose($file);

        return [$path, $prestaTotal];
    }

    protected function configure()
    {
        $this
            ->setDescription('SSA : envoyer des mails avec export permettant aux prestataires de facturer le ccas')
            ->addOption(
                'prestaid',
                null,
                InputOption::VALUE_REQUIRED,
                "Effectuer l'action pour un seul prestataire."
            )
            ->addOption(
                'yearmonth',
                null,
                InputOption::VALUE_REQUIRED,
                "Effectuer l'action pour le mois YYYYMM."
            );
    }

    /**
     * @param InputInterface  $input
     * @param OutputInterface $output
     *
     * @return int
     */
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $this->io = new SymfonyStyle($input, $output);
        $prestaid = $input->hasOption('prestaid') ? $input->getOption('prestaid') : null;
        $yearmonth = $input->hasOption('yearmonth') ? $input->getOption('yearmonth') : null;

        $prestaRepo = $this->em->getRepository(Prestataire::class);
        $prestas = $prestaid ? [0 => $prestaRepo->find($prestaid)] : $prestaRepo->findAll();

        $dateFormatter = new IntlDateFormatter('fr_FR', IntlDateFormatter::NONE, IntlDateFormatter::NONE);
        $dateFormatter->setPattern("MMMM Y");
        $date = new \DateTime();
        if ($yearmonth) {
            $year = substr($yearmonth, 0, 4);
            $month = intval(substr($yearmonth, 4, 2));
            $date->setDate(intval($year),$month,1);
            $monthYearStr = $dateFormatter->format($date);
        } else {
            $date->modify('previous month');
            $year = $date->format('Y');
            $month = intval($date->format('m'));
            $monthYearStr = $dateFormatter->format($date);
        }

        $this->io->title(
            'START. Préparation des exports pour ' . count($prestas) . ' prestataires '
            . 'contenant les transactions CCAS pour ' . $monthYearStr
        );

        //projectDir is composer.json
        /** @var KernelInterface $kernel */
        $kernel = $this->getApplication()->getKernel();
        $rootDir = $kernel->getContainer()->getParameter('kernel.project_dir');
        $dir = $rootDir . '/ccastransactions';
        if (!is_dir($dir)) {
            mkdir($dir, 0755, true);
        }

        $nf = new NumberFormatter('fr', NumberFormatter::SPELLOUT);

        foreach ($prestas as $p) {
            /* @var Prestataire $p */
            $data = $this->em->getRepository(Flux::class)->getValidCcasTransactionsByPrestaAndMonth($p, $month, $year);
            //only create export file if there are data
            $path = null;
            $prestaTotal = 0;
            if ($data) {
                list($path, $prestaTotal) = $this->createExportFile($year, $month, $p, $dir, $data, $nf);
            }
            //Send mail as soon as there are data or prestataire is CCAS OK
            //If there is no data, just inform there is nothing to do
            if ($data || $p->getCcasOk()) {
                $mail = $this->prepareMail($p, $monthYearStr, $prestaTotal);
                if ($path) {
                    $mail->attach(\Swift_Attachment::fromPath($path));
                }
                $this->mailer->send($mail);
            }
        }

        $this->io->success('End');

        $memoryUsage = memory_get_usage(true) / 1024 / 1024;
        $this->io->text("Batch finished with memory: ${memoryUsage}M");

        return 0;
    }

    private function currencySpellout($nf, $montant)
    {
        $amountInCents = round($montant * 100);
        $cents = round($amountInCents % 100, 2);
        $euros = ($amountInCents - $cents) / 100;
        $eurosText = $nf->format($euros) . ' euro' . ($euros > 1 ? 's' : '');
        $centsText = $cents ? ' et ' . $nf->format($cents) . ' centime' . ($cents > 0.01 ? 's' : '') : '';

        return $eurosText . $centsText;
    }
}
