<?php
namespace App\EventSubscriber;
use App\Enum\MercureToEnum;
use App\Enum\MercureTypeEnum;
use App\Event\CourseStateMachineEvent;
use App\Event\DeliveryWorkflowEvent;
use App\Event\WorkflowDeliverySubject;
use App\Service\DeliveryService;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Workflow\WorkflowInterface;
class WorkflowSubscriber implements EventSubscriberInterface
{
public function __construct(
private WorkflowInterface $deliveryWorkflow,
private WorkflowInterface $courseStateMachine,
private LoggerInterface $logger,
private DeliveryService $deliveryService
) {
}
public static function getSubscribedEvents(): array
{
return [
DeliveryWorkflowEvent::NAME => 'onStatusChange',
CourseStateMachineEvent::NAME => 'onCourseStatusChange',
];
}
/**
* @throws \Exception|\LogicException
*/
public function onStatusChange(DeliveryWorkflowEvent $event): void
{
$delivery = $event->getDelivery();
$transition = $event->getTransition();
$transitions = $this->deliveryWorkflow->getDefinition()->getTransitions();
$froms = [];
foreach ($transitions as $defTransition) {
if ($transition == $defTransition->getName()) {
$froms = $this->getRepresentation($defTransition->getFroms());
continue;
}
}
if (!$this->deliveryWorkflow->can(new WorkflowDeliverySubject($delivery, $froms), $transition)) {
$this->logger->critical(sprintf('Transition %s can\'t be done for delivery %s', $transition, $delivery->getId()));
throw new \Exception("Transition can't be done", Response::HTTP_BAD_REQUEST);
}
try {
$this->deliveryWorkflow->apply(new WorkflowDeliverySubject($delivery, $froms), $transition);
} catch (\LogicException $exception) {
throw $exception;
}
}
/**
* @throws \LogicException
*/
public function onCourseStatusChange(CourseStateMachineEvent $event): void
{
$course = $event->getCourse();
$transition = $event->getTransition();
if (!$recipient = $course->getDelivery()?->getRecipient()) {
throw new \Exception('Recipient not found', Response::HTTP_NOT_FOUND);
}
if (!$sender = $course->getDelivery()?->getSender()) {
throw new \Exception('Sender not found', Response::HTTP_NOT_FOUND);
}
if ($this->courseStateMachine->can($course, $transition)) {
try {
$this->courseStateMachine->apply($course, $transition);
$tos = [
['id' => $sender->getId(), 'to' => MercureToEnum::SENDER],
['id' => $recipient->getId(), 'to' => MercureToEnum::RECIPIENT],
];
foreach ($tos as $to) {
$this->deliveryService->publishMessage(
$to['id'],
$to['to'],
MercureTypeEnum::COURSE_STATUS_CHANGED,
[
'message' => sprintf('Course status change: %s', $transition),
'status' => $transition,
]
);
}
} catch (\LogicException $exception) {
throw $exception;
}
}
}
/**
* @param array<int, string> $froms
*
* @return array<string, int>
*/
public function getRepresentation(array $froms): array
{
$representation = [];
foreach ($froms as $key => $from) {
$representation[$from] = $key;
}
return $representation;
}
}