Covivo/mobicoop

View on GitHub
api/src/Carpool/Service/ProofManager.php

Summary

Maintainability
F
3 days
Test Coverage
<?php

/**
 * Copyright (c) 2020, MOBICOOP. All rights reserved.
 * This project is dual licensed under AGPL and proprietary licence.
 ***************************
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Affero General Public License as
 *    published by the Free Software Foundation, either version 3 of the
 *    License, or (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <gnu.org/licenses>.
 ***************************
 *    Licence MOBICOOP described in the file
 *    LICENSE
 */

namespace App\Carpool\Service;

use App\Carpool\Entity\Ask;
use App\Carpool\Entity\CarpoolProof;
use App\Carpool\Entity\Criteria;
use App\Carpool\Entity\Waypoint;
use App\Carpool\Event\CarpoolProofCertifyDropOffEvent;
use App\Carpool\Event\CarpoolProofCertifyPickUpEvent;
use App\Carpool\Event\CarpoolProofInvalidatedEvent;
use App\Carpool\Event\CarpoolProofValidatedEvent;
use App\Carpool\Exception\DynamicException;
use App\Carpool\Exception\ProofException;
use App\Carpool\Repository\AskRepository;
use App\Carpool\Repository\CarpoolProofRepository;
use App\Carpool\Repository\WaypointRepository;
use App\Carpool\Ressource\ClassicProof;
use App\DataProvider\Service\RpcApiManager;
use App\DataProvider\Service\RPCv3\Tools;
use App\Geography\Entity\Direction;
use App\Geography\Service\AddressCompleter;
use App\Geography\Service\Geocoder\GeocoderFactory;
use App\Geography\Service\GeoTools;
use App\Geography\Service\Point\GeocoderPointProvider;
use App\Incentive\Event\FirstShortDistanceJourneyPublishedEvent;
use App\Incentive\Service\Validation\JourneyValidation;
use App\Payment\Entity\PaymentProfile;
use App\Payment\Repository\PaymentProfileRepository;
use App\User\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * Carpool proof manager service, used to send proofs to a register.
 *
 * @author Sylvain Briat <sylvain.briat@mobicoop.org>
 */
class ProofManager
{
    private $entityManager;
    private $logger;
    private $provider;
    private $carpoolProofRepository;
    private $askRepository;
    private $waypointRepository;
    private $addressCompleter;
    private $geoTools;
    private $duration;
    private $minIdentityDistance;
    private $eventDispatcher;
    private $paymentProfileRepository;

    /**
     * @var JourneyValidation
     */
    private $_journeyValidation;

    /**
     * @var RpcApiManager
     */
    private $_rpcApiManager;

    private $_tools;

    /**
     * Constructor.
     *
     * @param EntityManagerInterface $entityManager          The entity manager
     * @param LoggerInterface        $logger                 The logger
     * @param CarpoolProofRepository $carpoolProofRepository The carpool proofs repository
     * @param AskRepository          $askRepository          The ask repository
     * @param WaypointRepository     $waypointRepository     The waypoint repository
     * @param GeoTools               $geoTools               The geotools
     * @param string                 $provider               The provider for proofs
     * @param int                    $duration               Number of days to send by default to the carpool register
     * @param int                    $minIdentityDistance    Minimal distance in meters between origin and destination/dropoff to determine distinct identities (C Class proof)
     */
    public function __construct(
        EntityManagerInterface $entityManager,
        LoggerInterface $logger,
        CarpoolProofRepository $carpoolProofRepository,
        AskRepository $askRepository,
        WaypointRepository $waypointRepository,
        GeocoderFactory $geocoderFactory,
        GeoTools $geoTools,
        EventDispatcherInterface $eventDispatcher,
        PaymentProfileRepository $paymentProfileRepository,
        JourneyValidation $journeyValidation,
        RpcApiManager $rpcApiManager,
        Tools $tools,
        string $provider,
        int $duration,
        int $minIdentityDistance
    ) {
        $this->entityManager = $entityManager;
        $this->logger = $logger;
        $this->carpoolProofRepository = $carpoolProofRepository;
        $this->askRepository = $askRepository;
        $this->waypointRepository = $waypointRepository;
        $this->geoTools = $geoTools;
        $this->duration = $duration;
        $this->minIdentityDistance = $minIdentityDistance;
        $this->eventDispatcher = $eventDispatcher;
        $this->paymentProfileRepository = $paymentProfileRepository;
        $this->_journeyValidation = $journeyValidation;
        $this->_rpcApiManager = $rpcApiManager;

        $this->addressCompleter = new AddressCompleter(new GeocoderPointProvider($geocoderFactory->getGeocoder()));

        switch ($provider) {
            case 'BetaGouv':
            default:
                $this->provider = $this->_rpcApiManager->getProvider();

                break;
        }
        $this->_tools = $tools;
    }

    private function __checkUpgradeToHigh(CarpoolProof $carpoolProof): CarpoolProof
    {
        if (is_null($carpoolProof->getDriver()) || is_null($carpoolProof->getPassenger())) {
            return $carpoolProof;
        }

        if (
            is_null($carpoolProof->getPickUpPassengerAddress())
            || is_null($carpoolProof->getDropOffPassengerAddress())
            || is_null($carpoolProof->getPickUpDriverAddress())
            || is_null($carpoolProof->getDropOffDriverAddress())
        ) {
            return $carpoolProof;
        }

        $pickUpPassengerAddress = $carpoolProof->getPickUpPassengerAddress();
        $dropOffPassengerAddress = $carpoolProof->getDropOffPassengerAddress();
        $pickUpDriverAddress = $carpoolProof->getPickUpDriverAddress();
        $dropOffDriverAddress = $carpoolProof->getDropOffDriverAddress();

        $pickUpsDistance = round($this->geoTools->get_distance_m($pickUpPassengerAddress->getLatitude(), $pickUpPassengerAddress->getLongitude(), $pickUpDriverAddress->getLatitude(), $pickUpDriverAddress->getLongitude()), 3);
        $dropOffsDistance = round($this->geoTools->get_distance_m($dropOffPassengerAddress->getLatitude(), $dropOffPassengerAddress->getLongitude(), $dropOffDriverAddress->getLatitude(), $dropOffDriverAddress->getLongitude()), 3);

        if (($pickUpsDistance > CarpoolProof::MINIMUM_DISTANCE_GPS_FOR_TYPE_HIGH) || ($dropOffsDistance > CarpoolProof::MINIMUM_DISTANCE_GPS_FOR_TYPE_HIGH)) {
            return $carpoolProof;
        }

        $driverPaymentProfile = $this->paymentProfileRepository->findBy(
            [
                'user' => $carpoolProof->getDriver(),
                'status' => PaymentProfile::STATUS_ACTIVE,
                'validationStatus' => PaymentProfile::VALIDATION_VALIDATED,
            ]
        );
        $passengerPaymentProfile = $this->paymentProfileRepository->findBy(
            [
                'user' => $carpoolProof->getPassenger(),
                'status' => PaymentProfile::STATUS_ACTIVE,
                'validationStatus' => PaymentProfile::VALIDATION_VALIDATED,
            ]
        );

        if (0 == count($driverPaymentProfile) || 0 == count($passengerPaymentProfile)) {
            return $carpoolProof;
        }

        if ($driverPaymentProfile[0]->getId() == $passengerPaymentProfile[0]->getId()) {
            return $carpoolProof;
        }

        $carpoolProof->setType(CarpoolProof::TYPE_HIGH);

        return $carpoolProof;
    }

    // PROOF MANAGEMENT

    /**
     * Get a carpool proof by its id.
     *
     * @param int $id The id of the proof
     *
     * @return CarpoolProof The carpool proof if found or null if not found
     */
    public function getProof(int $id)
    {
        return $this->carpoolProofRepository->find($id);
    }

    /**
     * Get a carpool proof by its id.
     *
     * @param int $id The id of the proof
     *
     * @return ClassicProof
     */
    public function getClassicProof(int $id)
    {
        $carpoolProof = $this->carpoolProofRepository->find($id);
        $classicProof = new ClassicProof();
        $classicProof->setId($id);
        $classicProof->setStatus(
            ($carpoolProof->getPickUpPassengerDate() ? '1' : '0').
            ($carpoolProof->getPickUpDriverDate() ? '1' : '0').
            ($carpoolProof->getDropOffPassengerDate() ? '1' : '0').
            ($carpoolProof->getDropOffDriverDate() ? '1' : '0')
        );
        $classicProof->setProofDate($carpoolProof->getStartDriverDate());

        return $classicProof;
    }

    /**
     * Get a proof for an Ask an a date.
     *
     * @param Ask       $ask  The ask
     * @param \DateTime $date The date
     *
     * @return null|CarpoolProof The carpool proof if it exists
     */
    public function getProofForDate(Ask $ask, \DateTime $date)
    {
        return $this->carpoolProofRepository->findByAskAndDate($ask, $date);
    }

    /**
     * Create a realtimeproof for an ask.
     *
     * @param Ask    $ask       The ask
     * @param float  $longitude The longitude of the author when the creation is asked
     * @param float  $latitude  The latitude of the author when the creation is asked
     * @param string $type      The type of proof
     * @param User   $author    The author of the proof
     * @param User   $driver    The driver
     * @param User   $passenger The passenger
     *
     * @return CarpoolProof The created proof
     */
    public function createProof(Ask $ask, float $longitude, float $latitude, string $type, User $author, User $driver, User $passenger, ?string $driverPhoneUniqueId = null, ?string $passengerPhoneUniqueId = null)
    {
        $carpoolProof = new CarpoolProof();
        $carpoolProof->setType($type);
        $carpoolProof->setAsk($ask);
        $carpoolProof->setDriver($driver);
        $carpoolProof->setPassenger($passenger);
        $carpoolProof->setDriverPhoneUniqueId($driverPhoneUniqueId);
        $carpoolProof->setPassengerPhoneUniqueId($passengerPhoneUniqueId);
        $originWaypoint = $this->waypointRepository->findMinPositionForAskAndRole($ask, Waypoint::ROLE_DRIVER);
        $destinationWaypoint = $this->waypointRepository->findMaxPositionForAskAndRole($ask, Waypoint::ROLE_DRIVER);
        $carpoolProof->setOriginDriverAddress(clone $originWaypoint->getAddress());
        $carpoolProof->setDestinationDriverAddress(clone $destinationWaypoint->getAddress());
        // we have to compute the start date of the driver
        if (Criteria::FREQUENCY_PUNCTUAL == $ask->getCriteria()->getFrequency()) {
            // for a punctual ad, we use fromDate and fromTime (both are theoretical, they *should* be correct !)
            /**
             * @var \DateTime $startDate
             */
            $startDate = clone $ask->getCriteria()->getFromDate();
            $startDate->setTime($ask->getCriteria()->getFromTime()->format('H'), $ask->getCriteria()->getFromTime()->format('i'));
        } else {
            // for a regular ad, we use the current date and the theoretical time
            $startDate = new \DateTime('UTC');

            switch ($startDate->format('w')) {
                // we check for each date of the period if it's a carpoool day
                case 0:     // sunday
                    if ($ask->getCriteria()->isSunCheck()) {
                        $startDate->setTime($ask->getCriteria()->getSunTime()->format('H'), $ask->getCriteria()->getSunTime()->format('i'));
                    }

                    break;

                case 1:     // monday
                    if ($ask->getCriteria()->isMonCheck()) {
                        $startDate->setTime($ask->getCriteria()->getMonTime()->format('H'), $ask->getCriteria()->getMonTime()->format('i'));
                    }

                    break;

                case 2:     // tuesday
                    if ($ask->getCriteria()->isTueCheck()) {
                        $startDate->setTime($ask->getCriteria()->getTueTime()->format('H'), $ask->getCriteria()->getTueTime()->format('i'));
                    }

                    break;

                case 3:     // wednesday
                    if ($ask->getCriteria()->isWedCheck()) {
                        $startDate->setTime($ask->getCriteria()->getWedTime()->format('H'), $ask->getCriteria()->getWedTime()->format('i'));
                    }

                    break;

                case 4:     // thursday
                    if ($ask->getCriteria()->isThuCheck()) {
                        $startDate->setTime($ask->getCriteria()->getThuTime()->format('H'), $ask->getCriteria()->getThuTime()->format('i'));
                    }

                    break;

                case 5:     // friday
                    if ($ask->getCriteria()->isFriCheck()) {
                        $startDate->setTime($ask->getCriteria()->getFriTime()->format('H'), $ask->getCriteria()->getFriTime()->format('i'));
                    }

                    break;

                case 6:     // saturday
                    if ($ask->getCriteria()->isSatCheck()) {
                        $startDate->setTime($ask->getCriteria()->getSatTime()->format('H'), $ask->getCriteria()->getSatTime()->format('i'));
                    }

                    break;
            }
        }
        $carpoolProof->setStartDriverDate($startDate);

        /**
         * @var \DateTime $endDate
         */
        // we init the end date with the start date
        $endDate = clone $startDate;
        // then we add the duration till the destination point
        $endDate->modify('+'.$destinationWaypoint->getDuration().' second');
        // note : for now, the end date is computed, it's theorEtical and not the 'real' end date
        $carpoolProof->setEndDriverDate($endDate);

        // direction
        $direction = new Direction();
        $direction->setDistance(0);
        $direction->setDuration(0);
        $direction->setDetail('');
        $direction->setSnapped('');
        $direction->setFormat('Dynamic');

        // search the role of the current user
        if ($author->getId() == $passenger->getId()) {
            // the author is the passenger
            $carpoolProof->setPickUpPassengerDate(new \DateTime('UTC'));
            $carpoolProof->setPickUpPassengerAddress($this->addressCompleter->getAddressByPartialAddressArray(['latitude' => $latitude, 'longitude' => $longitude]));
            $carpoolProof->setPoints([$carpoolProof->getPickUpPassengerAddress()]);
            $direction->setPoints([$carpoolProof->getPickUpPassengerAddress()]);
        } else {
            // the author is the driver
            $carpoolProof->setPickUpDriverDate(new \DateTime('UTC'));
            $carpoolProof->setPickUpDriverAddress($this->addressCompleter->getAddressByPartialAddressArray(['latitude' => $latitude, 'longitude' => $longitude]));
            $carpoolProof->setPoints([$carpoolProof->getPickUpDriverAddress()]);
            $direction->setPoints([$carpoolProof->getPickUpDriverAddress()]);
        }
        $carpoolProof->setDirection($direction);

        // Check for an already existing carpool proof for this journey base on StartDateDriver and same driver and passenger
        $duplicateCarpoolProof = $this->carpoolProofRepository->findForDuplicate($carpoolProof);

        if (is_null($duplicateCarpoolProof)) {
            $this->entityManager->persist($carpoolProof);
            $this->entityManager->flush();
        } else {
            $carpoolProof = $duplicateCarpoolProof;
        }

        if ($author->getId() == $passenger->getId()) {
            $event = new CarpoolProofCertifyPickUpEvent($carpoolProof, $driver);
            $this->eventDispatcher->dispatch(CarpoolProofCertifyPickUpEvent::NAME, $event);
        } else {
            $event = new CarpoolProofCertifyPickUpEvent($carpoolProof, $passenger);
            $this->eventDispatcher->dispatch(CarpoolProofCertifyPickUpEvent::NAME, $event);
        }

        return $carpoolProof;
    }

    /**
     * Update a proof.
     *
     * @param int   $id        The id of the proof to update
     * @param float $longitude The longitude of the author when the update is asked
     * @param float $latitude  The latitude of the author when the update is asked
     * @param User  $author    The author of the update
     * @param User  $passenger The passenger
     * @param int   $distance  The max distance between the driver and the passenger to validate the pickup/dropoff
     *
     * @return CarpoolProof The updated proof
     */
    public function updateProof(int $id, float $longitude, float $latitude, User $author, User $passenger, int $distance, ?string $driverPhoneUniqueId = null, ?string $passengerPhoneUniqueId = null)
    {
        // search the proof
        if (!$carpoolProof = $this->carpoolProofRepository->find($id)) {
            throw new ProofException('Proof not found');
        }

        // search the role of the current user
        $actor = null;
        if ($author->getId() == $passenger->getId()) {
            // the user is passenger
            $actor = CarpoolProof::ACTOR_PASSENGER;
        } else {
            // the user is driver
            $actor = CarpoolProof::ACTOR_DRIVER;
        }

        $firstDropOffCertification = false;

        // we perform different actions depending on the role and the moment
        switch ($actor) {
            case CarpoolProof::ACTOR_DRIVER:
                // uncomment this if dropoff is authorized only if both pickups has been made
                // if (!is_null($carpoolProof->getPickUpDriverAddress()) && is_null($carpoolProof->getPickUpPassengerAddress())) {
                //     // the driver can't set the dropoff while the passenger has not certified its pickup
                //     throw new ProofException("The passenger has not sent its pickup certification yet");
                // }
                if (!is_null($carpoolProof->getPickUpDriverAddress())) {
                    // the driver has set its pickup
                    if (!is_null($carpoolProof->getDropOffDriverAddress())) {
                        // the driver has already certified its pickup and dropoff
                        throw new ProofException('The driver has already sent its dropoff certification');
                    }
                    if (is_null($carpoolProof->getDropOffPassengerAddress())) {
                        // the passenger has not set its dropoff
                        $carpoolProof->setDropOffDriverDate(new \DateTime('UTC'));
                        $carpoolProof->setDropOffDriverAddress($this->addressCompleter->getAddressByPartialAddressArray(['latitude' => $latitude, 'longitude' => $longitude]));
                        $firstDropOffCertification = true;
                    } else {
                        // the passenger has set its dropoff, we have to check the positions
                        if ($this->geoTools->haversineGreatCircleDistance(
                            $latitude,
                            $longitude,
                            $carpoolProof->getDropOffPassengerAddress()->getLatitude(),
                            $carpoolProof->getDropOffPassengerAddress()->getLongitude()
                        ) <= $distance) {
                            // drop off driver
                            if (round(abs(strtotime((new \DateTime('UTC'))->format('Y-m-d h:i:s')) - strtotime($carpoolProof->getDropOffPassengerDate()->format('Y-m-d h:i:s'))) / 60, 2) > 2) {
                                throw new ProofException('Driver dropoff certification failed : the time between driver and passenger certifications exceeds 2 minutes');
                            }
                            $carpoolProof->setDropOffDriverDate(new \DateTime('UTC'));
                            $carpoolProof->setDropOffDriverAddress($this->addressCompleter->getAddressByPartialAddressArray(['latitude' => $latitude, 'longitude' => $longitude]));
                            // the driver and the passenger have made their certification, the proof is ready to be sent
                            $carpoolProof->setStatus(CarpoolProof::STATUS_PENDING);
                        // driver direction will be set when the dynamic ad of the driver will be finished
                        } else {
                            throw new ProofException('Driver dropoff certification failed : the passenger certified address is too far');
                        }
                    }
                } elseif (!is_null($carpoolProof->getPickUpPassengerAddress())) {
                    // the driver has not sent its pickup but the passenger has
                    if ($this->geoTools->haversineGreatCircleDistance(
                        $latitude,
                        $longitude,
                        $carpoolProof->getPickUpPassengerAddress()->getLatitude(),
                        $carpoolProof->getPickUpPassengerAddress()->getLongitude()
                    ) <= $distance) {
                        if (round(abs(strtotime((new \DateTime('UTC'))->format('Y-m-d h:i:s')) - strtotime($carpoolProof->getPickUpPassengerDate()->format('Y-m-d h:i:s'))) / 60, 2) > 2) {
                            throw new ProofException('Driver pickup certification failed : the time between driver and passenger certifications exceeds 2 minutes');
                        }
                        $carpoolProof->setPickupDriverDate(new \DateTime('UTC'));
                        $carpoolProof->setPickUpDriverAddress($this->addressCompleter->getAddressByPartialAddressArray(['latitude' => $latitude, 'longitude' => $longitude]));
                    } else {
                        throw new ProofException('Driver pickup certification failed : the passenger certified address is too far');
                    }
                } else {
                    // the passenger has not set its pickup
                    $carpoolProof->setPickUpDriverDate(new \DateTime('UTC'));
                    $carpoolProof->setPickUpDriverAddress($this->addressCompleter->getAddressByPartialAddressArray(['latitude' => $latitude, 'longitude' => $longitude]));
                }

                break;

            case CarpoolProof::ACTOR_PASSENGER:
                // uncomment this if dropoff is authorized only if both pickups has been made
                // if (!is_null($carpoolProof->getPickUpPassengerAddress()) && is_null($carpoolProof->getPickUpDriverAddress())) {
                //     // the passenger can't set the dropoff while the driver has not certified its pickup
                //     throw new ProofException("The driver has not sent its pickup certification yet");
                // }
                if (!is_null($carpoolProof->getPickUpPassengerAddress())) {
                    // the passenger has set its pickup
                    if (!is_null($carpoolProof->getDropOffPassengerAddress())) {
                        // the passenger has already certified its pickup and dropoff
                        throw new ProofException('The passenger has already sent its dropoff certification');
                    }
                    if (is_null($carpoolProof->getDropOffDriverAddress())) {
                        // the driver has not set its dropoff
                        $carpoolProof->setDropOffPassengerDate(new \DateTime('UTC'));
                        $carpoolProof->setDropOffPassengerAddress($this->addressCompleter->getAddressByPartialAddressArray(['latitude' => $latitude, 'longitude' => $longitude]));
                        // set the passenger dynamic ad to finished if relevant
                        if ($carpoolProof->getAsk()->getMatching()->getProposalRequest()->isDynamic()) {
                            $carpoolProof->getAsk()->getMatching()->getProposalRequest()->setFinished(true);
                            $this->entityManager->persist($carpoolProof->getAsk()->getMatching()->getProposalRequest());
                        }
                        $firstDropOffCertification = true;
                    } else {
                        // the driver has set its dropoff, we have to check the positions
                        if ($this->geoTools->haversineGreatCircleDistance(
                            $latitude,
                            $longitude,
                            $carpoolProof->getDropOffDriverAddress()->getLatitude(),
                            $carpoolProof->getDropOffDriverAddress()->getLongitude()
                        ) <= $distance) {
                            // drop off passenger
                            if (round(abs(strtotime((new \DateTime('UTC'))->format('Y-m-d h:i:s')) - strtotime($carpoolProof->getDropOffDriverDate()->format('Y-m-d h:i:s'))) / 60, 2) > 2) {
                                throw new ProofException('Passenger dropoff certification failed : the time between driver and passenger certifications exceeds 2 minutes');
                            }
                            $carpoolProof->setDropOffPassengerDate(new \DateTime('UTC'));
                            $carpoolProof->setDropOffPassengerAddress($this->addressCompleter->getAddressByPartialAddressArray(['latitude' => $latitude, 'longitude' => $longitude]));
                            // set the passenger dynamic ad to finished if relevant
                            if ($carpoolProof->getAsk()->getMatching()->getProposalRequest()->isDynamic()) {
                                $carpoolProof->getAsk()->getMatching()->getProposalRequest()->setFinished(true);
                                $this->entityManager->persist($carpoolProof->getAsk()->getMatching()->getProposalRequest());
                            }
                            // the driver and the passenger have made their certification, the proof is ready to be sent
                            $carpoolProof->setStatus(CarpoolProof::STATUS_PENDING);
                        } else {
                            throw new ProofException('Passenger dropoff certification failed : the driver certified address is too far');
                        }
                    }
                } elseif (!is_null($carpoolProof->getPickUpDriverAddress())) {
                    // the passenger has not sent its pickup but the driver has
                    if ($this->geoTools->haversineGreatCircleDistance(
                        $latitude,
                        $longitude,
                        $carpoolProof->getPickUpDriverAddress()->getLatitude(),
                        $carpoolProof->getPickUpDriverAddress()->getLongitude()
                    ) <= $distance) {
                        if (round(abs(strtotime((new \DateTime('UTC'))->format('Y-m-d h:i:s')) - strtotime($carpoolProof->getPickUpDriverDate()->format('Y-m-d h:i:s'))) / 60, 2) > 2) {
                            throw new ProofException('Passenger pickup certification failed : the time between driver and passenger certifications exceeds 2 minutes');
                        }
                        $carpoolProof->setPickupPassengerDate(new \DateTime('UTC'));
                        $carpoolProof->setPickUpPassengerAddress($this->addressCompleter->getAddressByPartialAddressArray(['latitude' => $latitude, 'longitude' => $longitude]));
                    } else {
                        throw new ProofException('Passenger pickup certification failed : the driver certified address is too far');
                    }
                } else {
                    // the driver has not set its pickup
                    $carpoolProof->setPickupPassengerDate(new \DateTime('UTC'));
                    $carpoolProof->setPickUpPassengerAddress($this->addressCompleter->getAddressByPartialAddressArray(['latitude' => $latitude, 'longitude' => $longitude]));
                }

                break;
        }

        if (is_null($carpoolProof->getDriverPhoneUniqueId())) {
            $carpoolProof->setDriverPhoneUniqueId($driverPhoneUniqueId);
        }
        if (is_null($carpoolProof->getPassengerPhoneUniqueId())) {
            $carpoolProof->setPassengerPhoneUniqueId($passengerPhoneUniqueId);
        }

        $this->entityManager->persist($carpoolProof);
        $this->entityManager->flush();

        if ($this->_journeyValidation->isStartedJourneyValidShortECCJourney($carpoolProof)) {
            $event = new FirstShortDistanceJourneyPublishedEvent($carpoolProof);
            $this->eventDispatcher->dispatch(FirstShortDistanceJourneyPublishedEvent::NAME, $event);
        }

        if ($firstDropOffCertification) {
            if (CarpoolProof::ACTOR_PASSENGER == $actor) {
                $event = new CarpoolProofCertifyDropOffEvent($carpoolProof, $carpoolProof->getDriver());
                $this->eventDispatcher->dispatch(CarpoolProofCertifyDropOffEvent::NAME, $event);
            } else {
                $event = new CarpoolProofCertifyDropOffEvent($carpoolProof, $carpoolProof->getPassenger());
                $this->eventDispatcher->dispatch(CarpoolProofCertifyDropOffEvent::NAME, $event);
            }
        }

        return $carpoolProof;
    }

    /**
     * Remove proofs of a user.
     * Used to anonymize proofs when a user deletes its account.
     *
     * @param User $user The user to delete
     */
    public function removeProofs(User $user)
    {
        // we start by searching in the asks, if the user is the first to remove its account
        $asks = $this->askRepository->findAskByUser($user);
        foreach ($asks as $ask) {
            if ($ask->getMatching()->getProposalOffer()->getUser()->getId() == $user->getId()) {
                // the user is the driver
                foreach ($ask->getCarpoolProofs() as $carpoolProof) {
                    // @var CarpoolProof $carpoolProof
                    $carpoolProof->setDriver(null);
                    $ask->removeCarpoolProof($carpoolProof);
                    // if the proof is pending, we set it to canceled
                    if (CarpoolProof::STATUS_PENDING == $carpoolProof->getStatus()) {
                        $carpoolProof->setStatus(CarpoolProof::STATUS_CANCELED);
                    }
                    // uncomment the following to anonymize driver addresses used in the proof
                    // $carpoolProof->setOriginDriverAddress(null);
                    // $carpoolProof->setDestinationDriverAddress(null);
                    $this->entityManager->persist($carpoolProof);
                }
            } else {
                foreach ($ask->getCarpoolProofs() as $carpoolProof) {
                    // @var CarpoolProof $carpoolProof
                    $carpoolProof->setPassenger(null);
                    $ask->removeCarpoolProof($carpoolProof);
                    // if the proof is pending, we set it to canceled
                    if (CarpoolProof::STATUS_PENDING == $carpoolProof->getStatus()) {
                        $carpoolProof->setStatus(CarpoolProof::STATUS_CANCELED);
                    }
                    // uncomment the following to anonymize passenger addresses used in the proof
                    // $carpoolProof->setPickUpPassengerAddress(null);
                    // $carpoolProof->setPickUpDriverAddress(null);
                    // $carpoolProof->setDropOffPassengerAddress(null);
                    // $carpoolProof->setDropOffDriverAddress(null);
                    $this->entityManager->persist($carpoolProof);
                }
            }
        }

        // then we search in the proofs, as another user may have removed its account, the proofs may still exist
        $carpoolProofs = $this->carpoolProofRepository->findRemainingByUser($user);
        foreach ($carpoolProofs as $carpoolProof) {
            /**
             * @var CarpoolProof $carpoolProof
             */
            if (!is_null($carpoolProof->getDriver())) {
                $carpoolProof->setDriver(null);
            // uncomment the following to anonymize driver addresses used in the proof
            // $carpoolProof->setOriginDriverAddress(null);
            // $carpoolProof->setDestinationDriverAddress(null);
            } elseif (!is_null($carpoolProof->getPassenger())) {
                $carpoolProof->setPassenger(null);
                // uncomment the following to anonymize passenger addresses used in the proof
                // $carpoolProof->setPickUpPassengerAddress(null);
                // $carpoolProof->setPickUpDriverAddress(null);
                // $carpoolProof->setDropOffPassengerAddress(null);
                // $carpoolProof->setDropOffDriverAddress(null);
            }
            $this->entityManager->persist($carpoolProof);
        }

        // the flush should be made elsewhere...
        // $this->entityManager->flush();
    }

    // PROOF REGISTER MANAGEMENT

    /**
     * Send the pending proofs.
     *
     * @param null|\DateTime $fromDate The start of the period for which we want to send the proofs
     * @param null|\DateTime $toDate   The end of the period  for which we want to send the proofs
     *
     * @return int The number of proofs sent
     */
    public function sendProofs(?\DateTime $fromDate = null, ?\DateTime $toDate = null)
    {
        // if no dates are sent, we use the last {duration} days
        if (is_null($fromDate)) {
            $fromDate = new \DateTime();
            $fromDate->modify('-'.$this->duration.' day');
            $fromDate->setTime(0, 0);
        }
        if (is_null($toDate)) {
            $toDate = new \DateTime();
            $toDate->modify('-1 day');
            $toDate->setTime(23, 59, 59, 999);
        }

        // first we need to validate the waiting proofs : some proof may be in undeterminate status
        $this->validateProofs($fromDate, $toDate);

        // then we get the pending proofs
        $proofs = $this->getProofs($fromDate, $toDate);
        $nbSent = 0;
        // exit;
        // send these proofs

        // we check taht we have phone numbers
        foreach ($proofs as $proof) {
            $this->_tools->setCurrentCarpoolProof($proof);

            if (is_null($proof->getDriver()->getTelephone())
                || '' == trim($proof->getDriver()->getTelephone())
                || is_null($proof->getPassenger()->getTelephone())
                || '' == trim($proof->getPassenger()->getTelephone())
            ) {
                $proof->setStatus(CarpoolProof::STATUS_NOT_SENT_MISSING_PHONE);
                $this->entityManager->persist($proof);
            }

            /**
             * @var CarpoolProof $proof
             */
            if (CarpoolProof::STATUS_PENDING !== $proof->getStatus()) {
                continue;
            }

            $now = new \DateTime();
            $startDateTime = new \DateTime($this->_tools->getStartTimeGeopoint()['datetime']);

            if ($startDateTime > $now) {
                continue;
            }

            $result = $this->provider->postCollection($proof, $this->provider::RESSOURCE_POST);
            $this->logger->info('Result of the send for proof #'.$proof->getId().' : code '.$result->getCode().' | value : '.$result->getValue());

            switch ($result) {
                case 200 == $result->getCode():
                    $proof->setStatus(CarpoolProof::STATUS_SENT);
                    ++$nbSent;

                    break;

                case 409 == $result->getCode():
                    $proof->setStatus(CarpoolProof::STATUS_SENT);

                    break;

                case 0 == $result->getCode():
                    $proof->setStatus(CarpoolProof::STATUS_RPC_NOT_REACHABLE);

                    break;

                default:
                    $proof->setStatus(CarpoolProof::STATUS_ERROR);

                    break;
            }
            $this->entityManager->persist($proof);
        }
        $this->entityManager->flush();

        return $nbSent;
    }

    public function checkProofs()
    {
        $proofs = $this->carpoolProofRepository->findCarpoolProofToCheck([CarpoolProof::STATUS_UNDER_CHECKING, CarpoolProof::STATUS_SENT]);
        $nbChecked = 0;

        foreach ($proofs as $proof) {
            /**
             * @var CarpoolProof $proof
             */
            $result = $this->provider->getCarpoolProof($proof, $this->provider::RESSOURCE_GET_ITEM);

            if (200 == $result->getCode()) {
                $data = json_decode($result->getValue(), true);

                $status = $this->_rpcApiManager->isVersion(RpcApiManager::RPC_API_V2)
                    ? $data['result']['data']['status']
                    : $data['status'];

                switch ($status) {
                    case 'acquisition_error':
                        $proof->setStatus(CarpoolProof::STATUS_ACQUISITION_ERROR);

                        break;

                    case 'normalization_error':
                        $proof->setStatus(CarpoolProof::STATUS_NORMALIZATION_ERROR);

                        break;

                    case 'fraudcheck_error':
                        $proof->setStatus(CarpoolProof::STATUS_FRAUD_ERROR);

                        break;

                    case 'ok':
                        $proof->setStatus(CarpoolProof::STATUS_VALIDATED);

                        break;

                    case 'expired':
                        $proof->setStatus(CarpoolProof::STATUS_EXPIRED);

                        break;

                    case 'canceled':
                        $proof->setStatus(CarpoolProof::STATUS_CANCELED_BY_OPERATOR);

                        break;

                    case 'pending':
                        $proof->setStatus(CarpoolProof::STATUS_UNDER_CHECKING);

                        break;

                    case 'unknown':
                        $proof->setStatus(CarpoolProof::STATUS_UNKNOWN);

                        break;

                    case 'anomaly_error':
                        $proof->setStatus(CarpoolProof::STATUS_ANOMALY_ERROR);

                        break;
                }
                ++$nbChecked;
            } else {
                $proof->setStatus(CarpoolProof::STATUS_ERROR);
            }
            $this->entityManager->persist($proof);
            $this->entityManager->flush();

            if (CarpoolProof::STATUS_VALIDATED === $proof->getStatus()) {
                $event = new CarpoolProofValidatedEvent($proof);
                $this->eventDispatcher->dispatch(CarpoolProofValidatedEvent::NAME, $event);
            } elseif (in_array($proof->getStatus(), CarpoolProof::ERROR_STATUS)) {
                $event = new CarpoolProofInvalidatedEvent($proof);
                $this->eventDispatcher->dispatch(CarpoolProofInvalidatedEvent::NAME, $event);
            }
        }

        return $nbChecked;
    }

    public function manualSendCarpoolProof($status = CarpoolProof::STATUS_PENDING)
    {
        $this->logger->info('********** MANUAL SENDING *************');
        $carpoolProofs = $this->carpoolProofRepository->findBy(['status' => $status]);
        $types = [CarpoolProof::TYPE_LOW, CarpoolProof::TYPE_MID, CarpoolProof::TYPE_HIGH];
        foreach ($carpoolProofs as $carpoolProof) {
            if (
                !is_null($carpoolProof->getAsk())
                && in_array($carpoolProof->getType(), $types)
            ) {
                $result = $this->provider->postCollection($carpoolProof, $this->provider::RESSOURCE_POST);
                $this->logger->info('Result of the send for proof #'.$carpoolProof->getId().' : code '.$result->getCode().' | value : '.$result->getValue());

                switch ($result) {
                    case 200 == $result->getCode():
                        $carpoolProof->setStatus(CarpoolProof::STATUS_SENT);

                        break;

                    case 409 == $result->getCode():
                        $carpoolProof->setStatus(CarpoolProof::STATUS_SENT);

                        break;

                    case 0 == $result->getCode():
                        $carpoolProof->setStatus(CarpoolProof::STATUS_RPC_NOT_REACHABLE);

                        break;

                    default:
                        $carpoolProof->setStatus(CarpoolProof::STATUS_ERROR);

                        break;
                }
                $this->entityManager->persist($carpoolProof);
            }
        }
        $this->entityManager->flush();
        $this->logger->info('********** END MANUAL SENDING *************');
    }

    public function proofAntifraudCheck(CarpoolProof $proof)
    {
        $this->_tools->setCurrentCarpoolProof($proof);

        if (!$this->proofSameDeviceCheck($proof)) {
            return $proof;
        }

        if (!$this->proofConcurrentSchedulesCheck($proof)) {
            return $proof;
        }

        if (!$this->proofSplittedTripCheck($proof)) {
            return $proof;
        }

        if (!$this->minimalDistanceCheck($proof)) {
            return $proof;
        }

        if (!$this->minimalTimeBetweenDriverJourneyCheck($proof)) {
            return $proof;
        }

        if (!$this->maximumOperatorTripIdPerDayCheck($proof)) {
            return $proof;
        }

        return $proof;
    }

    public function maximumOperatorTripIdPerDayCheck(CarpoolProof $proof): bool
    {
        $lastProofs = $this->carpoolProofRepository->findLastCarpoolProofOfUser($proof->getDriver());

        $operatorTripIds = [$this->_tools->computeOperatorTripId($proof)];
        foreach ($lastProofs as $lastProof) {
            $operatorTripId = $this->_tools->computeOperatorTripId($lastProof);
            if (!in_array($operatorTripId, $operatorTripIds)) {
                $operatorTripIds[] = $operatorTripId;
            }
        }

        if (count($operatorTripIds) > CarpoolProof::MAX_OPERATOR_TRIP_IDS) {
            return false;
        }

        return true;
    }

    public function minimalTimeBetweenDriverJourneyCheck(CarpoolProof $proof): bool
    {
        $lastProofs = $this->carpoolProofRepository->findLastCarpoolProofOfUser($proof->getDriver());

        foreach ($lastProofs as $lastProof) {
            $interval = $proof->getStartDriverDate()->diff($lastProof->getStartDriverDate());
            if ($interval->i < CarpoolProof::CONCURRENT_DRIVER_JOURNEY_THRESHOLD && 0 == $interval->days) {
                // echo 'less than '.CarpoolProof::CONCURRENT_DRIVER_JOURNEY_THRESHOLD.' minutes between current proof ('.$proof->getStartDriverDate()->format('d/m/y H:i:s').') and the old proof id = '.$lastProof->getId().' '.$lastProof->getStartDriverDate()->format('d/m/y H:i:s').PHP_EOL;

                $proof->setStatus(CarpoolProof::STATUS_CONCURRENT_DRIVER_JOURNEY);

                $this->entityManager->persist($proof);
                $this->entityManager->flush();

                return false;
            }
        }

        return true;
    }

    public function minimalDistanceCheck(CarpoolProof $proof): bool
    {
        if ($this->_tools->getDistance() < CarpoolProof::MINIMUM_DISTANCE_VALID_JOURNEY) {
            $proof->setStatus(CarpoolProof::STATUS_DISTANCE_TOO_SHORT);

            $this->entityManager->persist($proof);
            $this->entityManager->flush();

            return false;
        }

        return true;
    }

    public function proofConcurrentSchedulesCheck(CarpoolProof $proof)
    {
        $concurrentProofs = $this->carpoolProofRepository->getConcurrentProofs($proof);

        if (count($concurrentProofs) > 0) {
            $proof->setStatus(CarpoolProof::STATUS_INVALID_CONCURRENT_SCHEDULES);
            $this->entityManager->persist($proof);
            $this->entityManager->flush();

            return false;
        }

        return true;
    }

    public function proofSplittedTripCheck(CarpoolProof $proof)
    {
        $splittedTripProofs = $this->carpoolProofRepository->getSplittedTripProofs($proof);

        if (count($splittedTripProofs) > 0) {
            $proof->setStatus(CarpoolProof::STATUS_INVALID_SPLITTED_TRIP);
            $this->entityManager->persist($proof);
            $this->entityManager->flush();

            return false;
        }

        return true;
    }

    public function proofSameDeviceCheck(CarpoolProof $proof)
    {
        if (!is_null($proof->getDriverPhoneUniqueId()) && !is_null($proof->getPassengerPhoneUniqueId()) && $proof->getDriverPhoneUniqueId() == $proof->getPassengerPhoneUniqueId()) {
            $proof->setStatus(CarpoolProof::STATUS_INVALID_DUPLICATE_DEVICE);
            $this->entityManager->persist($proof);
            $this->entityManager->flush();

            return false;
        }

        return true;
    }

    /**
     * Validate the pending proofs for the given period.
     * Used to update pending with undetermined final class.
     *
     * @param \DateTime $fromDate The start of the period for which we want to update the proofs
     * @param \DateTime $toDate   The end of the period  for which we want to update the proofs
     */
    private function validateProofs(\DateTime $fromDate, \DateTime $toDate)
    {
        // first we search the undetermined proofs for the given period
        $carpoolProofs = $this->carpoolProofRepository->findByTypesAndPeriod([CarpoolProof::TYPE_UNDETERMINED_CLASSIC, CarpoolProof::TYPE_UNDETERMINED_DYNAMIC], $fromDate, $toDate);

        if (is_array($carpoolProofs) && count($carpoolProofs) > 0) {
            // then we determine the right class depending on the available data
            foreach ($carpoolProofs as $carpoolProof) {
                // we change the status to pending
                $carpoolProof->setStatus(CarpoolProof::STATUS_PENDING);

                // For now, TYPE_HIGH is only for dynamique because on organized trip we use declarative origin and destination not GPS given
                if (
                    CarpoolProof::TYPE_UNDETERMINED_DYNAMIC == $carpoolProof->getType()
                    && !is_null($carpoolProof->getPickUpDriverAddress())
                    && !is_null($carpoolProof->getPickUpPassengerAddress())
                    && !is_null($carpoolProof->getDropOffDriverAddress())
                    && !is_null($carpoolProof->getDropOffPassengerAddress())
                    && !is_null($carpoolProof->getPickUpDriverDate())
                    && !is_null($carpoolProof->getPickUpPassengerDate())
                    && !is_null($carpoolProof->getDropOffDriverDate())
                    && !is_null($carpoolProof->getDropOffPassengerDate())
                    && $this->checkDistinctIdentities($carpoolProof)) {
                    // all the possible data is set for both carpoolers => max type
                    $carpoolProof->setType(CarpoolProof::TYPE_HIGH);
                    $this->entityManager->persist($carpoolProof);

                    continue;
                }
                if (
                    !is_null($carpoolProof->getPickUpDriverAddress())
                    && !is_null($carpoolProof->getDropOffDriverAddress())
                    && !is_null($carpoolProof->getPickUpDriverDate())
                    && !is_null($carpoolProof->getDropOffDriverDate())
                    && !is_null($carpoolProof->getAsk())) {
                    // all the possible data is set for the driver => middle type
                    $carpoolProof->setType(CarpoolProof::TYPE_MID);
                    // we need to fill/replace the passenger time details with theoretical data and driver data
                    $pickUpWaypoint = $this->waypointRepository->findMinPositionForAskAndRole($carpoolProof->getAsk(), Waypoint::ROLE_PASSENGER);
                    $dropOffWaypoint = $this->waypointRepository->findMaxPositionForAskAndRole($carpoolProof->getAsk(), Waypoint::ROLE_PASSENGER);

                    /**
                     * @var \Datetime $pickUpDate
                     */
                    // we init the pickup date with the start date of the driver
                    $pickUpDate = clone $carpoolProof->getStartDriverDate();
                    // then we add the duration till the pickup point
                    $pickUpDate->modify('+'.$pickUpWaypoint->getDuration().' second');

                    /**
                     * @var \Datetime $dropOffDate
                     */
                    // we init the dropoff date with the start date of the driver
                    $dropOffDate = clone $carpoolProof->getStartDriverDate();
                    // then we add the duration till the dropoff point
                    $dropOffDate->modify('+'.$dropOffWaypoint->getDuration().' second');
                    $carpoolProof->setPickUpPassengerDate($pickUpDate);
                    $carpoolProof->setDropOffPassengerDate($dropOffDate);

                    // The proof meets de B criteria, we check if it could meet de C criteria
                    $carpoolProof = $this->__checkUpgradeToHigh($carpoolProof);

                    $this->entityManager->persist($carpoolProof);

                    continue;
                }
                if (
                    !is_null($carpoolProof->getPickUpPassengerAddress())
                    && !is_null($carpoolProof->getDropOffPassengerAddress())
                    && !is_null($carpoolProof->getPickUpPassengerDate())
                    && !is_null($carpoolProof->getDropOffPassengerDate())) {
                    // all the possible data is set for the passenger => middle type
                    // the driver basic information are already filled (they are filled at the carpool proof creation)
                    // we only keep the time information as the geographical data are not validated
                    $carpoolProof->setOriginDriverAddress(null);
                    $carpoolProof->setDestinationDriverAddress(null);
                    $carpoolProof->setType(CarpoolProof::TYPE_MID);

                    // The proof meets de B criteria, we check if it could meet de C criteria
                    $carpoolProof = $this->__checkUpgradeToHigh($carpoolProof);

                    $this->entityManager->persist($carpoolProof);

                    continue;
                }
                // if any of the previous is verified, we initialize with the lowest possible type
                $carpoolProof->setType(CarpoolProof::TYPE_LOW);
                $this->entityManager->persist($carpoolProof);
                $this->entityManager->flush();
            }
            $this->entityManager->flush();
        }
    }

    /**
     * Check if the two carpoolProof's actors are not the same person
     * The actors' origins must be distant enough Or The driver's destination and the passenger's drop off must be distant enough.
     */
    private function checkDistinctIdentities(CarpoolProof $carpoolProof): bool
    {
        if (is_null($carpoolProof->getAsk())) {
            return false;
        }

        $originsDistantEnough = $destinationsDistantEnough = false;

        // The actors' origins

        $originDriverAddress = $carpoolProof->getOriginDriverAddress();

        if ($carpoolProof->getAsk()->getMatching()->getProposalRequest()->getUser()->getId() == $carpoolProof->getPassenger()->getId()) {
            $originPassengerAddress = $carpoolProof->getAsk()->getMatching()->getProposalRequest()->getWaypoints()[0];
        } elseif ($carpoolProof->getAsk()->getMatching()->getProposalOffer()->getUser()->getId() == $carpoolProof->getPassenger()->getId()) {
            $originPassengerAddress = $carpoolProof->getAsk()->getMatching()->getProposalOffer()->getWaypoints()[0];
        } else {
            throw new DynamicException("Passenger can't be found");
        }

        if ($this->geoTools->haversineGreatCircleDistance($originDriverAddress->getLatitude(), $originDriverAddress->getLongitude(), $originPassengerAddress->getAddress()->getLatitude(), $originPassengerAddress->getAddress()->getLongitude()) >= $this->minIdentityDistance) {
            $originsDistantEnough = true;
        }

        // The driver's destination and the passenger's drop off
        $destinationDriverAddress = $carpoolProof->getDestinationDriverAddress();
        if (is_null($destinationDriverAddress)) {
            throw new DynamicException('No destination driver address');
        }
        $dropOffPassengerAddress = $carpoolProof->getDropOffPassengerAddress();
        if (is_null($dropOffPassengerAddress)) {
            throw new DynamicException('No dropoff passenger address');
        }
        if ($this->geoTools->haversineGreatCircleDistance($destinationDriverAddress->getLatitude(), $destinationDriverAddress->getLongitude(), $dropOffPassengerAddress->getLatitude(), $dropOffPassengerAddress->getLongitude()) >= $this->minIdentityDistance) {
            $destinationsDistantEnough = true;
        }

        // The actors' origins must be distant enough Or The driver's destination and the passenger's drop off must be distant enough
        return $originsDistantEnough || $destinationsDistantEnough;
    }

    /**
     * Create and return the pending proofs for the given period.
     * Used to generate non-realtime proofs.
     *
     * @param \DateTime $fromDate The start of the period for which we want to get the proofs
     * @param \DateTime $toDate   The end of the period  for which we want to get the proofs
     *
     * @return array The proofs
     */
    private function getProofs(\DateTime $fromDate, \DateTime $toDate)
    {
        // first we search the pending asks for the given period
        $pendingAsks = $this->askRepository->findPendingAsksForPeriod($fromDate, $toDate);

        // then we search the accepted asks for the given period
        $acceptedAsks = $this->askRepository->findAcceptedAsksForPeriod($fromDate, $toDate);

        // we merge both arrays
        $asks = array_merge($pendingAsks, $acceptedAsks);

        // then we create the corresponding proofs
        foreach ($asks as $ask) {
            // we first check if both carpooler have a phone number, as it's mandatory !
            if (is_null($ask->getUser()->getTelephone())
                || '' == trim($ask->getUser()->getTelephone())
                || is_null($ask->getUserRelated()->getTelephone())
                || '' == trim($ask->getUserRelated()->getTelephone())
            ) {
                continue;
            }
            if (Criteria::FREQUENCY_PUNCTUAL == $ask->getCriteria()->getFrequency()) {
                // punctual, only one carpool proof
                // we search if a carpool proof already exists for the date
                if (!$this->carpoolProofRepository->findByAskAndDate($ask, $ask->getCriteria()->getFromDate())) {
                    // no carpool for this date, we create it
                    $carpoolProof = new CarpoolProof();
                    $carpoolProof->setStatus(CarpoolProof::STATUS_PENDING);
                    $carpoolProof->setType(CarpoolProof::TYPE_LOW);
                    $carpoolProof->setAsk($ask);
                    $carpoolProof->setDriver($ask->getMatching()->getProposalOffer()->getUser());
                    $carpoolProof->setPassenger($ask->getMatching()->getProposalRequest()->getUser());
                    $originWaypoint = $this->waypointRepository->findMinPositionForAskAndRole($ask, Waypoint::ROLE_DRIVER);
                    $destinationWaypoint = $this->waypointRepository->findMaxPositionForAskAndRole($ask, Waypoint::ROLE_DRIVER);
                    $carpoolProof->setOriginDriverAddress(clone $originWaypoint->getAddress());
                    $carpoolProof->setDestinationDriverAddress(clone $destinationWaypoint->getAddress());
                    $pickUpWaypoint = $this->waypointRepository->findMinPositionForAskAndRole($ask, Waypoint::ROLE_PASSENGER);
                    $dropOffWaypoint = $this->waypointRepository->findMaxPositionForAskAndRole($ask, Waypoint::ROLE_PASSENGER);
                    $carpoolProof->setPickUpPassengerAddress(clone $pickUpWaypoint->getAddress());
                    $carpoolProof->setDropOffPassengerAddress(clone $dropOffWaypoint->getAddress());

                    /**
                     * @var \Datetime $startDate
                     */
                    $startDate = clone $ask->getCriteria()->getFromDate();

                    $startTime = $ask->getCriteria()->getFromTime();
                    if (is_null($ask->getCriteria()->getFromTime())) {
                        if (!is_null($ask->getMatching()->getProposalRequest()->getCriteria()->getFromTime())) {
                            $startTime = $ask->getMatching()->getProposalRequest()->getCriteria()->getFromTime();
                        } elseif (!is_null($ask->getMatching()->getProposalOffer()->getCriteria()->getFromTime())) {
                            $startTime = $ask->getMatching()->getProposalOffer()->getCriteria()->getFromTime();
                        } else {
                            continue;
                        }
                    }

                    $startDate->setTime($startTime->format('H'), $startTime->format('i'));
                    $carpoolProof->setStartDriverDate($startDate);

                    /**
                     * @var \Datetime $endDate
                     */
                    // we init the end date with the start date
                    $endDate = clone $startDate;
                    // then we add the duration till the destination point
                    $endDate->modify('+'.$destinationWaypoint->getDuration().' second');
                    $carpoolProof->setEndDriverDate($endDate);

                    /**
                     * @var \Datetime $pickUpDate
                     */
                    // we init the pickup date with the start date of the driver
                    $pickUpDate = clone $ask->getCriteria()->getFromDate();
                    $pickUpDate->setTime($startTime->format('H'), $startTime->format('i'));
                    // then we add the duration till the pickup point
                    $pickUpDate->modify('+'.$pickUpWaypoint->getDuration().' second');

                    /**
                     * @var \Datetime $dropOffDate
                     */
                    // we init the dropoff date with the start date of the driver
                    $dropOffDate = clone $startDate;
                    // then we add the duration till the dropoff point
                    $dropOffDate->modify('+'.$dropOffWaypoint->getDuration().' second');
                    $carpoolProof->setPickUpPassengerDate($pickUpDate);
                    $carpoolProof->setDropOffPassengerDate($dropOffDate);
                    // Antifraud rpc check before sending
                    $this->proofAntifraudCheck($carpoolProof);

                    $this->entityManager->persist($carpoolProof);
                    $this->entityManager->flush();
                }
            } else {
                // regular, we need to create a carpool item for each day between fromDate (or the ask fromDate if it's after the given fromDate) and toDate
                $curDate = clone max($fromDate, $ask->getCriteria()->getFromDate());
                $continue = true;
                // we get some available information here outside the loop
                $originWaypoint = $this->waypointRepository->findMinPositionForAskAndRole($ask, Waypoint::ROLE_DRIVER);
                $destinationWaypoint = $this->waypointRepository->findMaxPositionForAskAndRole($ask, Waypoint::ROLE_DRIVER);
                $pickUpWaypoint = $this->waypointRepository->findMinPositionForAskAndRole($ask, Waypoint::ROLE_PASSENGER);
                $dropOffWaypoint = $this->waypointRepository->findMaxPositionForAskAndRole($ask, Waypoint::ROLE_PASSENGER);
                while ($continue) {
                    // we search if a carpool proof already exists for the date
                    if (!$this->carpoolProofRepository->findByAskAndDate($ask, $curDate)) {
                        // no carpool for this date, we create it if it's a carpool day
                        $carpoolDay = false;

                        /**
                         * @var \Datetime $startDate
                         */
                        $startDate = clone $curDate;

                        /**
                         * @var \Datetime $pickUpDate
                         */
                        // we init the pickup date with the start date of the driver
                        $pickUpDate = clone $curDate;

                        switch ($curDate->format('w')) {
                            // we check for each date of the period if it's a carpoool day
                            case 0:     // sunday
                                if ($ask->getCriteria()->isSunCheck()) {
                                    // we check if time is set, could not be the case if ask criteria is different than proposal or matching criteria
                                    // if time is not set we consider that the day is not carpooled
                                    if (is_null($ask->getCriteria()->getSunTime())) {
                                        break;
                                    }
                                    $carpoolDay = true;
                                    $startDate->setTime($ask->getCriteria()->getSunTime()->format('H'), $ask->getCriteria()->getSunTime()->format('i'));
                                    $pickUpDate->setTime($ask->getCriteria()->getSunTime()->format('H'), $ask->getCriteria()->getSunTime()->format('i'));
                                }

                                break;

                            case 1:     // monday
                                if ($ask->getCriteria()->isMonCheck()) {
                                    // we check if time is set, could not be the case if ask criteria is different than proposal or matching criteria
                                    // if time is not set we consider that the day is not carpooled
                                    if (is_null($ask->getCriteria()->getMonTime())) {
                                        break;
                                    }
                                    $carpoolDay = true;
                                    $startDate->setTime($ask->getCriteria()->getMonTime()->format('H'), $ask->getCriteria()->getMonTime()->format('i'));
                                    $pickUpDate->setTime($ask->getCriteria()->getMonTime()->format('H'), $ask->getCriteria()->getMonTime()->format('i'));
                                }

                                break;

                            case 2:     // tuesday
                                if ($ask->getCriteria()->isTueCheck()) {
                                    // we check if time is set, could not be the case if ask criteria is different than proposal or matching criteria
                                    // if time is not set we consider that the day is not carpooled
                                    if (is_null($ask->getCriteria()->getTueTime())) {
                                        break;
                                    }
                                    $carpoolDay = true;
                                    $startDate->setTime($ask->getCriteria()->getTueTime()->format('H'), $ask->getCriteria()->getTueTime()->format('i'));
                                    $pickUpDate->setTime($ask->getCriteria()->getTueTime()->format('H'), $ask->getCriteria()->getTueTime()->format('i'));
                                }

                                break;

                            case 3:     // wednesday
                                if ($ask->getCriteria()->isWedCheck()) {
                                    // we check if time is set, could not be the case if ask criteria is different than proposal or matching criteria
                                    // if time is not set we consider that the day is not carpooled
                                    if (is_null($ask->getCriteria()->getWedTime())) {
                                        break;
                                    }
                                    $carpoolDay = true;
                                    $startDate->setTime($ask->getCriteria()->getWedTime()->format('H'), $ask->getCriteria()->getWedTime()->format('i'));
                                    $pickUpDate->setTime($ask->getCriteria()->getWedTime()->format('H'), $ask->getCriteria()->getWedTime()->format('i'));
                                }

                                break;

                            case 4:     // thursday
                                if ($ask->getCriteria()->isThuCheck()) {
                                    // we check if time is set, could not be the case if ask criteria is different than proposal or matching criteria
                                    // if time is not set we consider that the day is not carpooled
                                    if (is_null($ask->getCriteria()->getThuTime())) {
                                        break;
                                    }
                                    $carpoolDay = true;
                                    $startDate->setTime($ask->getCriteria()->getThuTime()->format('H'), $ask->getCriteria()->getThuTime()->format('i'));
                                    $pickUpDate->setTime($ask->getCriteria()->getThuTime()->format('H'), $ask->getCriteria()->getThuTime()->format('i'));
                                }

                                break;

                            case 5:     // friday
                                if ($ask->getCriteria()->isFriCheck()) {
                                    // we check if time is set, could not be the case if ask criteria is different than proposal or matching criteria
                                    // if time is not set we consider that the day is not carpooled
                                    if (is_null($ask->getCriteria()->getFriTime())) {
                                        break;
                                    }
                                    $carpoolDay = true;
                                    $startDate->setTime($ask->getCriteria()->getFriTime()->format('H'), $ask->getCriteria()->getFriTime()->format('i'));
                                    $pickUpDate->setTime($ask->getCriteria()->getFriTime()->format('H'), $ask->getCriteria()->getFriTime()->format('i'));
                                }

                                break;

                            case 6:     // saturday
                                if ($ask->getCriteria()->isSatCheck()) {
                                    // we check if time is set, could not be the case if ask criteria is different than proposal or matching criteria
                                    // if time is not set we consider that the day is not carpooled
                                    if (is_null($ask->getCriteria()->getSatTime())) {
                                        break;
                                    }
                                    $carpoolDay = true;
                                    $startDate->setTime($ask->getCriteria()->getSatTime()->format('H'), $ask->getCriteria()->getSatTime()->format('i'));
                                    $pickUpDate->setTime($ask->getCriteria()->getSatTime()->format('H'), $ask->getCriteria()->getSatTime()->format('i'));
                                }

                                break;
                        }
                        if ($carpoolDay) {
                            $carpoolProof = new CarpoolProof();
                            $carpoolProof->setStatus(CarpoolProof::STATUS_PENDING);
                            $carpoolProof->setType(CarpoolProof::TYPE_LOW);
                            $carpoolProof->setAsk($ask);
                            $carpoolProof->setDriver($ask->getMatching()->getProposalOffer()->getUser());
                            $carpoolProof->setPassenger($ask->getMatching()->getProposalRequest()->getUser());
                            $carpoolProof->setOriginDriverAddress(clone $originWaypoint->getAddress());
                            $carpoolProof->setDestinationDriverAddress(clone $destinationWaypoint->getAddress());
                            $carpoolProof->setPickUpPassengerAddress(clone $pickUpWaypoint->getAddress());
                            $carpoolProof->setDropOffPassengerAddress(clone $dropOffWaypoint->getAddress());

                            $carpoolProof->setStartDriverDate($startDate);

                            /**
                             * @var \Datetime $endDate
                             */
                            // we init the end date with the start date
                            $endDate = clone $startDate;
                            // then we add the duration till the destination point
                            $endDate->modify('+'.$destinationWaypoint->getDuration().' second');
                            $carpoolProof->setEndDriverDate($endDate);
                            // we add the duration till the pickup point
                            $pickUpDate->modify('+'.$pickUpWaypoint->getDuration().' second');

                            /**
                             * @var \Datetime $dropOffDate
                             */
                            // we init the dropoff date with the start date of the driver
                            $dropOffDate = clone $startDate;
                            // then we add the duration till the dropoff point
                            $dropOffDate->modify('+'.$dropOffWaypoint->getDuration().' second');
                            $carpoolProof->setPickUpPassengerDate($pickUpDate);
                            $carpoolProof->setDropOffPassengerDate($dropOffDate);

                            // Check for an already existing carpool proof for this journey base on StartDateDriver and same driver and passenger
                            // Now useless ? $this->carpoolProofRepository->findByAskAndDate($ask, $curDate) better ?
                            // if (!is_null($this->carpoolProofRepository->findForDuplicate($carpoolProof))) {
                            //     $continue = false;

                            //     continue;
                            // }

                            // Antifraud rpc check before sending
                            $this->proofAntifraudCheck($carpoolProof);

                            $this->entityManager->persist($carpoolProof);
                            $this->entityManager->flush();

                            $continue = false;
                        }
                    }

                    if ($curDate->format('Y-m-d') == $toDate->format('Y-m-d')) {
                        $continue = false;
                    } else {
                        $curDate->modify('+1 day');
                    }
                }
            }
        }
        $this->entityManager->flush();

        // we return all the pending proofs
        return $this->carpoolProofRepository->findBy(['status' => [CarpoolProof::STATUS_PENDING, CarpoolProof::STATUS_RPC_NOT_REACHABLE]]);
    }
}