<?php
namespace App\Service;
use Throwable;
use JsonException;
use App\Adapter\AppointmentAdapter;
use App\Mapper\AppointmentMapper;
use App\Model\Appointment;
use App\Model\Garage;
use App\Model\Vehicle;
use App\Utils\Date;
use DateTime;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
class AppointmentService
{
public const PREFIX_NEW = 'new';
public const APPOINTMENT_SLUG = 'appointment';
protected AppointmentAdapter $adapter;
protected AppointmentMapper $mapper;
protected ClientService $clientService;
protected SerializerInterface $serializer;
public function __construct(
AppointmentAdapter $adapter,
AppointmentMapper $mapper,
ClientService $clientService,
SerializerInterface $serializer)
{
$this->adapter = $adapter;
$this->mapper = $mapper;
$this->clientService = $clientService;
$this->serializer = $serializer;
}
/**
* @throws Throwable
* @throws TransportExceptionInterface
* @throws ServerExceptionInterface
* @throws RedirectionExceptionInterface
* @throws DecodingExceptionInterface
* @throws ClientExceptionInterface
*/
public function getGarages(): ?array
{
return $this->adapter->getGarages();
}
public function getService(array $data): ?array
{
if (isset($data['brand']) && isset($data['model']) && isset($data['submodel'])) {
$services = array_unique($this->adapter->getServices($data), SORT_REGULAR);
// convert cents to euro
return array_map(function ($value) {
$value['price'] = $value['price'] / 100;
$value['description'] = html_entity_decode($value['description'], ENT_QUOTES|ENT_HTML5);
return $value;
}, $services);
}
return [];
}
public function getServiceIds(Appointment $appointment): array
{
$serviceIDs = [];
foreach ($appointment->getServices() as $service) {
$serviceIDs[] = $service['id'];
}
return $serviceIDs;
}
/**
* @throws RedirectionExceptionInterface
* @throws DecodingExceptionInterface
* @throws ClientExceptionInterface
* @throws JsonException
* @throws TransportExceptionInterface
* @throws Throwable
* @throws ServerExceptionInterface
*/
public function sendAppointment(Appointment $appointment, bool $isUserConnected = false): bool
{
$dto = $this->mapper->getAppointmentDto($appointment, $isUserConnected);
return $this->adapter->sendAppointment($dto, $isUserConnected);
}
/**
* @return void
*/
public function addToVehicles(array &$vehicles, Vehicle $vehicle)
{
// for update, remove element and add a new one later
if ($id = $vehicle->getId()) {
$this->deleteVehicle($id, $vehicles);
}
// add the encoded vehicle on the top, so it will be focused on front
array_unshift($vehicles, [
'selected' => true,
'id' => $vehicle->getId() ?? self::PREFIX_NEW.'_'.uniqid(),
'brand' => $vehicle->getBrand(),
'model' => $vehicle->getModel(),
'submodel' => $vehicle->getSubModel(),
'canUpdate' => $vehicle->canUpdate(),
'attributes' => [
[
'type' => 'select',
'label' => 'account.cars.brand',
'value' => $vehicle->getBrand(),
'field' => 'brand',
],
[
'type' => 'select',
'label' => 'account.cars.model',
'value' => $vehicle->getModel(),
'field' => 'model',
],
[
'type' => 'select',
'label' => 'account.cars.submodel',
'value' => $vehicle->getSubModel(),
'field' => 'submodel',
],
[
'type' => 'text',
'label' => 'account.cars.plate',
'value' => $vehicle->getPlate(),
'field' => 'plate',
],
[
'type' => 'text',
'label' => 'account.cars.vin',
'value' => $vehicle->getVin(),
'field' => 'vin',
],
[
'type' => 'link',
'label' => 'account.cars.doclinkreg',
'files' => $vehicle->getRegistrationFiles(),
'field' => 'registrationFiles',
],
[
'type' => 'link',
'label' => 'account.cars.doclinkconv',
'files' => $vehicle->getConversionFiles(),
'field' => 'conversionFiles',
],
],
]);
}
public function deleteVehicle(string $id, array &$vehicles): array
{
$found = array_search($id, array_column($vehicles, 'id'), true);
if (false !== $found && isset($vehicles[$found])) {
$temp = [];
foreach ($vehicles as $car) {
if ($car['id'] !== $id) {
$temp[] = $car;
}
}
$vehicles = $temp;
}
return $vehicles;
}
public function deleteAppointmentVehicle(string $id, bool $isUserConnected, SessionInterface $session): array
{
if ($isUserConnected) {
$this->clientService->deleteVehicle($id);
$userVehicles = $this->clientService->getClientVehicles();
$vehicles = [];
foreach ($userVehicles as $vehicle) {
$vehicle = $this->serializer->deserialize(json_encode($vehicle, JSON_THROW_ON_ERROR), Vehicle::class, 'json');
$this->addToVehicles($vehicles, $vehicle);
}
} else {
$vehicles = $session->get('vehicles');
$this->deleteVehicle($id, $vehicles);
$session->set('vehicles', $vehicles);
}
return $vehicles;
}
/**
* @return void
*/
public function removeNextStep(Appointment $appointment, string $currentStep)
{
switch ($currentStep) {
case 'garage':
$appointment->setVehicle(null);
$appointment->setServices(null);
$appointment->setSlot(null);
$appointment->setReceptionSlot(null);
$appointment->setDeliverySlot(null);
break;
case 'vehicle':
$appointment->setServices(null);
$appointment->setSlot(null);
$appointment->setReceptionSlot(null);
$appointment->setDeliverySlot(null);
break;
case 'service':
$appointment->setSlot(null);
$appointment->setReceptionSlot(null);
$appointment->setDeliverySlot(null);
break;
}
}
/**
* @return void
*/
public function removeAllSession(SessionInterface $session)
{
$session->remove('appointment');
$session->remove('vehicles');
$session->remove('gon');
$session->remove('maxStep');
}
public function needReinitVehicleStep(SessionInterface $session, ?string $id): bool
{
/** @var Appointment $appointment */
$appointment = $session->get('appointment');
if ($appointment && $appointment->getVehicle() && $appointment->getVehicle()->getId() == $id) {
$appointment->setVehicle(null);
$this->removeNextStep($appointment, 'vehicle');
$session->set('reinit', true);
return true;
}
return false;
}
public function changeGarage($garageID, SessionInterface $session, array &$responseDatas, SlotService $slotService): array
{
$garages = $session->get('garages');
/** @var Appointment $appointment */
$appointment = $session->get('appointment');
$hours = [];
$garage = $this->getSelectedGarage($garages, $garageID);
$day = $responseDatas['day'];
$datestart = new DateTime("first day of $day");
$dateend = new DateTime("last day of $day");
$data = [
'serviceIDs' => implode(',', $slotService->getServiceIds($appointment)),
'garageID' => $garage->getId(),
'from' => $datestart->getTimestamp(),
'to' => $dateend->getTimestamp()
];
$slots = $slotService->getAvailability($data, $appointment);
$garage->setAvailabilities($slots);
$appointment->setGarage($garage);
if (isset($slots[$responseDatas['day']])) {
$hours = $slots[$responseDatas['day']];
}
$dropTime = [
'day' => $responseDatas['day'],
'hour' => $responseDatas['time'],
'from' => $datestart->getTimestamp(),
'to' => $dateend->getTimestamp()
];
$receptionSlots = $slotService->getDropPickUpSlot($appointment, $dropTime);
$deliverySlots = $slotService->getDropPickUpSlot($appointment, $dropTime, false);
$responseDatas = array_merge($responseDatas, [
'receptionTime' => $receptionSlots,
'deliveryTime' => $deliverySlots,
'hours' => $hours,
]);
return $responseDatas;
}
/**
* Get datas from Service such as duration and list services
*
* @param Appointment $appointment
* @param array $responseDatas
* @return void
*/
public function getDatasFromServices(Appointment $appointment, array &$responseDatas = [])
{
$services = $appointment->getServices();
$listServices = [];
foreach ($services as $service) {
$listServices[] = $service['name'];
}
$responseDatas['duration'] = Date::getElementsTime($this->getTotalServiceDuration($services));
$responseDatas['list_services'] = implode(', ', $listServices);
}
/**
* @param array $services
* @return int
*/
public function getTotalServiceDuration(array $services): int
{
$totalDuration = 0;
foreach ($services as $service) {
$totalDuration += $service['time'];
}
return $totalDuration;
}
/**
* @param $garages
* @param $garageID
* @return Garage
* @throws JsonException
*/
public function getSelectedGarage($garages, $garageID): ?Garage
{
$garage = null;
foreach ($garages as $center) {
if ($garageID == $center['id']) {
/** @var Garage $garage */
$garage = $this->serializer->deserialize(json_encode($center, JSON_THROW_ON_ERROR), Garage::class, 'json');
break;
}
}
return $garage;
}
/**
* @param Appointment $appointment
* @return array
*/
public function getServiceHeaderInfo(Appointment $appointment): array
{
// services calcul
$serviceHeaderInfo = [];
$pipe = '';
$serviceHeaderInfo['total'] = 0;
$serviceHeaderInfo['name'] = '';
foreach ($appointment->getServices() as $key => $service) {
if ($key > 0) {
$pipe = ' | ';
}
$serviceHeaderInfo['name'] .= $pipe . $service['name'];
$serviceHeaderInfo['total'] += $service['price'];
}
$appointment->setServiceHeaderInfo($serviceHeaderInfo);
return $serviceHeaderInfo;
}
}