<?php
namespace App\EventSubscriber;
use ApiPlatform\Symfony\EventListener\EventPriorities;
use App\Entity\Job;
use App\Entity\Schedule;
use App\Entity\User;
use App\Service\Message;
use Doctrine\Persistence\ManagerRegistry;
use JetBrains\PhpStorm\ArrayShape;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\ViewEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use function array_multisort;
use function current;
use function floor;
use function in_array;
final class JobOnWaySubscriber implements EventSubscriberInterface
{
/** @var string The relative path inside templates/messages without any extensions */
private const TEMPLATE = 'job/on-way';
public function __construct(
private readonly TokenStorageInterface $tokenStorage,
private readonly ManagerRegistry $managerRegistry,
private readonly Message $messageService,
)
{
}
#[ArrayShape([KernelEvents::VIEW => "array"])]
public static function getSubscribedEvents(): array
{
return [
KernelEvents::VIEW => ['sendMessage', EventPriorities::POST_WRITE],
];
}
public function sendMessage(ViewEvent $event): void
{
$job = $event->getControllerResult();
$method = $event->getRequest()->getMethod();
# PUT and PATCH methods are used to update instances, and we are only interested in updated jobs
if (!$job instanceof Job || !in_array($method, [Request::METHOD_PUT, Request::METHOD_PATCH])) {
return;
}
$previousData = $event->getRequest()->get('previous_data');
# If the status hasn't changed, we're not interested
if ($previousData->getStatus() === $job->getStatus()) {
return;
}
# If the status hasn't changed to "on way", we're not interested
if ($job->getStatus() !== Job::JOB_STATUS_ON_WAY) {
return;
}
$customer = $job->getPond()->getCustomer();
$schedule = $this->getLatestSchedule($job);
$message = $this->messageService
->withRecipient($customer)
->withSubject('We are on our way')
->withTemplate(self::TEMPLATE)
->withTemplateData([
'addressLine1' => $job->getPond()->getAddress1(),
'firstName' => $customer->getFirstName() === 'ANNETTE' ? null : $customer->getFirstName(),
'fullAddress' => $job->getPond()->getAddress(),
'travelTime' => $this->formatTime($schedule->getEstimatedTravelTime()),
]);
$currentUser = $this->tokenStorage->getToken()->getUser();
if ($currentUser instanceof User) {
$message = $message->withCurrentUser($currentUser);
}
$message->send();
}
private function getLatestSchedule(Job $job): Schedule
{
/** @var iterable<int, Schedule> $schedules */
$schedules = $this->managerRegistry->getRepository(Schedule::class)
->findBy(['job' => $job]);
$sort = [];
foreach ($schedules as $schedule) {
$sort[] = $schedule->getDate()->getTimestamp();
}
array_multisort($schedules, SORT_DESC, $sort);
return current($schedules);
}
private function formatTime(int $time): string
{
$parts = [];
$days = floor($time / (60 * 60 * 24));
if ($days > 0) {
$parts[] = sprintf('%d day%s', $days, $days > 1 ? 's' : '');
$time -= $days * (60 * 60 * 24);
}
$hours = floor($time / (60 * 60));
if ($hours > 0) {
$parts[] = sprintf('%d hour%s', $hours, $hours > 1 ? 's' : '');
$time -= $hours * (60 * 60);
}
$minutes = floor($time / 60);
if ($minutes > 0) {
$parts[] = sprintf('%d minute%s', $minutes, $minutes > 1 ? 's' : '');
}
if (count($parts) === 0) {
return '1 minute';
}
return implode(', ', $parts);
}
}