<?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\tests\Service\Messenger\OnGenerationFailsTest;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Repository\StoredObjectRepositoryInterface;
use Chill\MainBundle\Repository\UserRepositoryInterface;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;
use Symfony\Contracts\Translation\TranslatorInterface;

// use Symfony\Component\Translation\LocaleSwitcher;

/**
 * @see OnGenerationFailsTest for test suite
 */
final readonly class OnGenerationFails implements EventSubscriberInterface
{
    public const LOG_PREFIX = '[docgen failed] ';

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

    public static function getSubscribedEvents()
    {
        return [
            WorkerMessageFailedEvent::class => 'onMessageFailed',
        ];
    }

    public function onMessageFailed(WorkerMessageFailedEvent $event): void
    {
        if ($event->willRetry()) {
            return;
        }

        $message = $event->getEnvelope()->getMessage();

        if (!$message instanceof RequestGenerationMessage) {
            return;
        }

        $this->logger->error(self::LOG_PREFIX.'Docgen failed', [
            'stored_object_id' => $message->getDestinationStoredObjectId(),
            'entity_id' => $message->getEntityId(),
            'template_id' => $message->getTemplateId(),
            'creator_id' => $message->getCreatorId(),
            'throwable_class' => $event->getThrowable()::class,
        ]);

        $this->markObjectAsFailed($message);
        $this->warnCreator($message, $event);
    }

    private function markObjectAsFailed(RequestGenerationMessage $message): void
    {
        $object = $this->storedObjectRepository->find($message->getDestinationStoredObjectId());

        if (null === $object) {
            $this->logger->error(self::LOG_PREFIX.'Stored object not found', ['stored_object_id', $message->getDestinationStoredObjectId()]);
        }

        $object->setStatus(StoredObject::STATUS_FAILURE);

        $this->entityManager->flush();
    }

    private function warnCreator(RequestGenerationMessage $message, WorkerMessageFailedEvent $event): void
    {
        if (null === $message->getSendResultToEmail() || '' === $message->getSendResultToEmail()) {
            $this->logger->info(self::LOG_PREFIX.'No email associated with this request generation');

            return;
        }

        // if the exception is not a GeneratorException, we try the previous one...
        $throwable = $event->getThrowable();
        if (!$throwable instanceof GeneratorException) {
            $throwable = $throwable->getPrevious() ?? $throwable;
        }

        if ($throwable instanceof GeneratorException) {
            $errors = $throwable->getErrors();
        } else {
            $errors = [$throwable->getTraceAsString()];
        }

        if (null === $template = $this->docGeneratorTemplateRepository->find($message->getTemplateId())) {
            $this->logger->info(self::LOG_PREFIX.'Template not found', ['template_id' => $message->getTemplateId()]);

            return;
        }

        if (null === $creator = $this->userRepository->find($message->getCreatorId())) {
            $this->logger->error(self::LOG_PREFIX.'Creator not found');

            return;
        }

        // Implementation with LocaleSwitcher (commented out - to be activated after migration to sf7.2):
        /*
        $this->localeSwitcher->runWithLocale($creator->getLocale(), function () use ($message, $errors, $template, $creator) {
            $email = (new TemplatedEmail())
                ->to($message->getSendResultToEmail())
                ->subject($this->translator->trans('docgen.failure_email.The generation of a document failed'))
                ->textTemplate('@ChillDocGenerator/Email/on_generation_failed_email.txt.twig')
                ->context([
                    'errors' => $errors,
                    'template' => $template,
                    'creator' => $creator,
                    'stored_object_id' => $message->getDestinationStoredObjectId(),
                ]);

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

        // Current implementation:
        $email = (new TemplatedEmail())
            ->to($message->getSendResultToEmail())
            ->subject($this->translator->trans('docgen.failure_email.The generation of a document failed'))
            ->textTemplate('@ChillDocGenerator/Email/on_generation_failed_email.txt.twig')
            ->context([
                'errors' => $errors,
                'template' => $template,
                'creator' => $creator,
                'stored_object_id' => $message->getDestinationStoredObjectId(),
            ]);

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