<?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\DocGeneratorTemplateRepository;
use Chill\DocGeneratorBundle\Service\Generator\Generator;
use Chill\DocGeneratorBundle\Service\Generator\GeneratorException;
use Chill\DocStoreBundle\AsyncUpload\TempUrlGeneratorInterface;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Exception\StoredObjectManagerException;
use Chill\DocStoreBundle\Repository\StoredObjectRepository;
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;

/**
 * Handle the request of document generation.
 */
#[\Symfony\Component\Messenger\Attribute\AsMessageHandler]
class RequestGenerationHandler
{
    final public const AUTHORIZED_TRIALS = 5;
    private const LOG_PREFIX = '[docgen message handler] ';
    public function __construct(
        private readonly DocGeneratorTemplateRepository $docGeneratorTemplateRepository,
        private readonly EntityManagerInterface $entityManager,
        private readonly Generator $generator,
        private readonly LoggerInterface $logger,
        private readonly StoredObjectRepository $storedObjectRepository,
        private readonly UserRepositoryInterface $userRepository,
        private readonly MailerInterface $mailer,
        private readonly TempUrlGeneratorInterface $tempUrlGenerator,
        private readonly TranslatorInterface $translator,
    ) {}
    public function __invoke(RequestGenerationMessage $message): void
    {
        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 {
                $destinationStoredObject = $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
    {
        $url = $this->tempUrlGenerator->generate('GET', $destinationStoredObject->getFilename(), 3600);
        $parts = [];
        parse_str(parse_url($url->url)['query'], $parts);
        $validity = \DateTimeImmutable::createFromFormat('U', $parts['temp_url_expires']);

        $email = (new TemplatedEmail())
            ->to($message->getSendResultToEmail())
            ->textTemplate('@ChillDocGenerator/Email/send_data_dump_to_admin.txt.twig')
            ->context([
                'link' => $url->url,
                'validity' => $validity,
            ])
            ->subject($this->translator->trans('docgen.data_dump_email.subject'));

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