<?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\MainBundle\Export\Messenger;

use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\MainBundle\Entity\ExportGeneration;
use Chill\MainBundle\Repository\ExportGenerationRepository;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;

class OnExportGenerationFails implements EventSubscriberInterface
{
    private const LOG_PREFIX = '[export_generation failed] ';

    public function __construct(
        private readonly LoggerInterface $logger,
        private readonly ExportGenerationRepository $repository,
        private readonly EntityManagerInterface $entityManager,
    ) {}

    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 ExportRequestGenerationMessage) {
            return;
        }

        if (null === $exportGeneration = $this->repository->find($message->id)) {
            throw new \UnexpectedValueException('ExportRequestGenerationMessage not found');
        }

        $this->logger->error(self::LOG_PREFIX.'ExportRequestGenerationMessage failed to execute generation', [
            'exportId' => (string) $message->id,
            'userId' => $message->userId,
            'alias' => $exportGeneration->getExportAlias(),
            'throwable_message' => $event->getThrowable()->getMessage(),
            'throwable_trace' => $event->getThrowable()->getTraceAsString(),
            'throwable' => $event->getThrowable()::class,
            'full_generation_duration_failure' => microtime(true) - $exportGeneration->getCreatedAt()->getTimestamp(),
        ]);

        $this->markObjectAsFailed($event, $exportGeneration);
        $this->entityManager->flush();
    }

    private function markObjectAsFailed(WorkerMessageFailedEvent $event, ExportGeneration $exportGeneration): void
    {
        $exportGeneration->getStoredObject()->addGenerationErrors($event->getThrowable()->getMessage());
        $exportGeneration->getStoredObject()->setStatus(StoredObject::STATUS_FAILURE);
    }
}
