<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\DocGeneratorBundle\Service\Messenger;

use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepositoryInterface;
use Chill\DocGeneratorBundle\Service\Generator\GeneratorException;
use Chill\DocGeneratorBundle\Service\Generator\GeneratorInterface;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Exception\StoredObjectManagerException;
use Chill\DocStoreBundle\Repository\StoredObjectRepositoryInterface;
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
use Chill\MainBundle\Repository\UserRepositoryInterface;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Messenger\Exception\UnrecoverableMessageHandlingException;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

// use Symfony\Component\Translation\LocaleSwitcher;

/**
 * Handle the request of document generation.
 */
class RequestGenerationHandler implements MessageHandlerInterface
{
    final public const AUTHORIZED_TRIALS = 5;

    private const LOG_PREFIX = '[docgen message handler] ';

    public function __construct(
        private readonly DocGeneratorTemplateRepositoryInterface $docGeneratorTemplateRepository,
        private readonly EntityManagerInterface $entityManager,
        private readonly GeneratorInterface $generator,
        private readonly LoggerInterface $logger,
        private readonly StoredObjectRepositoryInterface $storedObjectRepository,
        private readonly UserRepositoryInterface $userRepository,
        private readonly MailerInterface $mailer,
        private readonly TranslatorInterface $translator,
        private readonly StoredObjectManagerInterface $storedObjectManager,
        // private readonly LocaleSwitcher $localeSwitcher,
    ) {}

    public function __invoke(RequestGenerationMessage $message)
    {
        if (null === $template = $this->docGeneratorTemplateRepository->find($message->getTemplateId())) {
            throw new \RuntimeException('template not found: '.$message->getTemplateId());
        }

        if (null === $destinationStoredObject = $this->storedObjectRepository->find($message->getDestinationStoredObjectId())) {
            throw new \RuntimeException('destination stored object not found : '.$message->getDestinationStoredObjectId());
        }

        if ($destinationStoredObject->getGenerationTrialsCounter() >= self::AUTHORIZED_TRIALS) {
            $this->logger->error(self::LOG_PREFIX.'Request generation abandoned: maximum number of retry reached', [
                'template_id' => $message->getTemplateId(),
                'destination_stored_object' => $message->getDestinationStoredObjectId(),
                'trial' => $destinationStoredObject->getGenerationTrialsCounter(),
            ]);

            throw new UnrecoverableMessageHandlingException('maximum number of retry reached');
        }

        $creator = $this->userRepository->find($message->getCreatorId());

        // we increase the number of generation trial in the object, and, in the same time, update the counter
        // on the database side. This ensure that, if the script fails for any reason (memory limit reached), the
        // counter is inscreased
        $destinationStoredObject->addGenerationTrial();
        $this->entityManager->createQuery('UPDATE '.StoredObject::class.' s SET s.generationTrialsCounter = s.generationTrialsCounter + 1 WHERE s.id = :id')
            ->setParameter('id', $destinationStoredObject->getId())
            ->execute();

        try {
            if ($message->isDumpOnly()) {
                $destinationStoredObject = $this->generator->generateDataDump(
                    $template,
                    $message->getEntityId(),
                    $message->getContextGenerationData(),
                    $destinationStoredObject,
                    $creator
                );

                $this->sendDataDump($destinationStoredObject, $message);
            } else {
                $this->generator->generateDocFromTemplate(
                    $template,
                    $message->getEntityId(),
                    $message->getContextGenerationData(),
                    $destinationStoredObject,
                    $creator
                );
            }
        } catch (StoredObjectManagerException|GeneratorException $e) {
            $this->entityManager->flush();

            $this->logger->error(self::LOG_PREFIX.'Request generation failed', [
                'template_id' => $message->getTemplateId(),
                'destination_stored_object' => $message->getDestinationStoredObjectId(),
                'trial' => $destinationStoredObject->getGenerationTrialsCounter(),
                'error' => $e->getTraceAsString(),
            ]);

            throw $e;
        }

        $this->entityManager->flush();

        $this->logger->info(self::LOG_PREFIX.'Request generation finished', [
            'template_id' => $message->getTemplateId(),
            'destination_stored_object' => $message->getDestinationStoredObjectId(),
            'duration_int' => (new \DateTimeImmutable('now'))->getTimestamp() - $message->getCreatedAt()->getTimestamp(),
        ]);
    }

    private function sendDataDump(StoredObject $destinationStoredObject, RequestGenerationMessage $message): void
    {
        // Implementation with LocaleSwitcher (commented out - to be activated after migration to sf7.2):
        // Note: This method sends emails to admin addresses, not user addresses, so locale switching may not be needed
        /*
        $this->localeSwitcher->runWithLocale('fr', function () use ($destinationStoredObject, $message) {
            // Get the content of the document
            $content = $this->storedObjectManager->read($destinationStoredObject);
            $filename = $destinationStoredObject->getFilename();
            $contentType = $destinationStoredObject->getType();

            // Create the email with the document as an attachment
            $email = (new TemplatedEmail())
                ->to($message->getSendResultToEmail())
                ->textTemplate('@ChillDocGenerator/Email/send_data_dump_to_admin.txt.twig')
                ->context([
                    'filename' => $filename,
                ])
                ->subject($this->translator->trans('docgen.data_dump_email.subject'))
                ->attach($content, $filename, $contentType);

            $this->mailer->send($email);
        });
        */

        // Current implementation:
        // Get the content of the document
        $content = $this->storedObjectManager->read($destinationStoredObject);
        $filename = $destinationStoredObject->getFilename();
        $contentType = $destinationStoredObject->getType();

        // Create the email with the document as an attachment
        $email = (new TemplatedEmail())
            ->to($message->getSendResultToEmail())
            ->textTemplate('@ChillDocGenerator/Email/send_data_dump_to_admin.txt.twig')
            ->context([
                'filename' => $filename,
            ])
            ->subject($this->translator->trans('docgen.data_dump_email.subject'))
            ->attach($content, $filename, $contentType);

        $this->mailer->send($email);
    }
}
