Covivo/mobicoop

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

Summary

Maintainability
F
3 days
Test Coverage
<?php

/**
 * Copyright (c) 2019, 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\Action\Event\ActionEvent;
use App\Action\Repository\ActionRepository;
use App\Carpool\Entity\Ask;
use App\Carpool\Entity\AskHistory;
use App\Carpool\Entity\Criteria;
use App\Carpool\Entity\Matching;
use App\Carpool\Entity\Proposal;
use App\Carpool\Event\AskAcceptedEvent;
use App\Carpool\Event\AskPostedEvent;
use App\Carpool\Event\AskRefusedEvent;
use App\Carpool\Event\AskUpdatedEvent;
use App\Carpool\Exception\AdException;
use App\Carpool\Repository\AskRepository;
use App\Carpool\Repository\CarpoolProofRepository;
use App\Carpool\Repository\MatchingRepository;
use App\Carpool\Ressource\Ad;
use App\Communication\Service\InternalMessageManager;
use App\Payment\Entity\CarpoolItem;
use App\Payment\Entity\WeekItem;
use App\Payment\Exception\PaymentException;
use App\Payment\Repository\CarpoolItemRepository;
use App\Solidary\Entity\SolidaryAsk;
use App\Solidary\Entity\SolidaryAskHistory;
use App\User\Entity\User;
use App\User\Exception\BlockException;
use App\User\Service\BlockManager;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Security\Core\Security;

/**
 * Ask manager service.
 *
 * @author Sylvain Briat <sylvain.briat@covivo.eu>
 */
class AskManager
{
    private $eventDispatcher;
    private $entityManager;
    private $matchingRepository;
    private $askRepository;
    private $resultManager;
    private $logger;
    private $security;
    private $carpoolItemRepository;
    private $carpoolProofRepository;
    private $paymentActive;
    private $paymentActiveDate;
    private $blockManager;
    private $actionRepository;
    private $internalMessageManager;

    /**
     * Constructor.
     */
    public function __construct(
        EventDispatcherInterface $eventDispatcher,
        EntityManagerInterface $entityManager,
        MatchingRepository $matchingRepository,
        AskRepository $askRepository,
        ResultManager $resultManager,
        LoggerInterface $logger,
        Security $security,
        CarpoolItemRepository $carpoolItemRepository,
        CarpoolProofRepository $carpoolProofRepository,
        BlockManager $blockManager,
        ActionRepository $actionRepository,
        string $paymentActive,
        InternalMessageManager $internalMessageManager
    ) {
        $this->eventDispatcher = $eventDispatcher;
        $this->entityManager = $entityManager;
        $this->matchingRepository = $matchingRepository;
        $this->askRepository = $askRepository;
        $this->resultManager = $resultManager;
        $this->logger = $logger;
        $this->security = $security;
        $this->carpoolItemRepository = $carpoolItemRepository;
        $this->carpoolProofRepository = $carpoolProofRepository;
        $this->paymentActive = false;
        if ($this->paymentActiveDate = \DateTime::createFromFormat('Y-m-d', $paymentActive)) {
            $this->paymentActiveDate->setTime(0, 0);
            $this->paymentActive = true;
        }
        $this->blockManager = $blockManager;
        $this->actionRepository = $actionRepository;
        $this->internalMessageManager = $internalMessageManager;
    }

    /**
     * Get an ask by its id.
     *
     * @param int $id The id of the ask to find
     *
     * @return Ask The ask found or null if not found
     */
    public function getAsk(int $id)
    {
        return $this->askRepository->find($id);
    }

    /**
     * Create an ask.
     */
    public function createAsk(Ask $ask)
    {
        // todo : check if an ask already exists for the match and the proposals

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

        // dispatch en event
        // $event = new AskPostedEvent($ask);
        // $this->eventDispatcher->dispatch(AskPostedEvent::NAME, $event);
        return $ask;
    }

    /**
     * Update an ask.
     */
    public function updateAsk(Ask $ask)
    {
        // todo : check if an ask already exists for the match and the proposals

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

        $this->createAssociatedAskHistory($ask);

        // dispatch en event
        $event = new AskUpdatedEvent($ask);
        $this->eventDispatcher->dispatch(AskUpdatedEvent::NAME, $event);

        return $ask;
    }

    /**
     * Create an ask from an ad.
     *
     * @param Ad   $ad     The ad used to create the ask
     * @param bool $formal The ask is a formal ask
     *
     * @return Ad
     */
    public function createAskFromAd(Ad $ad, bool $formal)
    {
        $ask = new Ask();
        $matching = $this->matchingRepository->find($ad->getMatchingId());

        // We check that the user isn't matching with himself
        if ($matching->getProposalRequest()->getUser()->getId() == $matching->getProposalOffer()->getUser()->getId()) {
            throw new AdException(AdException::SELF_MATCHING);
        }

        if ($ad->getAdId() == $matching->getProposalOffer()->getId()) {
            // the carpooler is the driver, the requester is the passenger
            $ask->setType(Proposal::TYPE_ONE_WAY == $matching->getProposalRequest()->getType() ? Proposal::TYPE_ONE_WAY : Proposal::TYPE_OUTWARD);
            $ask->setUser($matching->getProposalRequest()->getUser());
        } else {
            // the carpooler is the passenger, the requester is the driver
            $ask->setType(Proposal::TYPE_ONE_WAY == $matching->getProposalOffer()->getType() ? Proposal::TYPE_ONE_WAY : Proposal::TYPE_OUTWARD);
            $ask->setUser($matching->getProposalOffer()->getUser());
        }

        if ($formal) {
            // if it's a formal ask, the status is pending, depending on the role
            $ask->setStatus(Ad::ROLE_DRIVER == $ad->getRole() ? Ask::STATUS_PENDING_AS_DRIVER : Ask::STATUS_PENDING_AS_PASSENGER);
        } else {
            // if it's not a formal ask, the status is initiated
            $ask->setStatus(Ask::STATUS_INITIATED);
        }

        $ask->setMatching($matching);

        // we use the matching criteria
        $criteria = clone $matching->getCriteria();

        // we treat the outward
        // for regular trips we need to check the dates and days
        if (Criteria::FREQUENCY_REGULAR == $matching->getCriteria()->getFrequency()) {
            if ($ad->getOutwardDate()) {
                $criteria->setFromDate($ad->getOutwardDate());
            }
            if ($ad->getOutwardLimitDate()) {
                $criteria->setToDate($ad->getOutwardLimitDate());
            }
            // we init the original schedule
            $criteria->setMonCheck(false);
            $criteria->setTueCheck(false);
            $criteria->setWedCheck(false);
            $criteria->setThuCheck(false);
            $criteria->setFriCheck(false);
            $criteria->setSatCheck(false);
            $criteria->setSunCheck(false);
            // we use the driver times (the passenger times will be computed using these times and the chosen days)
            $criteria->setMonTime($matching->getProposalOffer()->getCriteria()->getMonTime());
            $criteria->setTueTime($matching->getProposalOffer()->getCriteria()->getTueTime());
            $criteria->setWedTime($matching->getProposalOffer()->getCriteria()->getWedTime());
            $criteria->setThuTime($matching->getProposalOffer()->getCriteria()->getThuTime());
            $criteria->setFriTime($matching->getProposalOffer()->getCriteria()->getFriTime());
            $criteria->setSatTime($matching->getProposalOffer()->getCriteria()->getSatTime());
            $criteria->setSunTime($matching->getProposalOffer()->getCriteria()->getSunTime());

            if (Ad::ROLE_DRIVER_OR_PASSENGER != $ad->getRole()) {
                // we fill the selected days if a role has been set
                foreach ($ad->getSchedule() as $schedule) {
                    if (isset($schedule['outwardTime']) && '' != $schedule['outwardTime']) {
                        if (isset($schedule['mon']) && $schedule['mon']) {
                            $criteria->setMonCheck(true);
                            $criteria->setMonTime(new \DateTime($schedule['outwardTime']));
                        }
                        if (isset($schedule['tue']) && $schedule['tue']) {
                            $criteria->setTueCheck(true);
                            $criteria->setTueTime(new \DateTime($schedule['outwardTime']));
                        }
                        if (isset($schedule['wed']) && $schedule['wed']) {
                            $criteria->setWedCheck(true);
                            $criteria->setWedTime(new \DateTime($schedule['outwardTime']));
                        }
                        if (isset($schedule['thu']) && $schedule['thu']) {
                            $criteria->setThuCheck(true);
                            $criteria->setThuTime(new \DateTime($schedule['outwardTime']));
                        }
                        if (isset($schedule['fri']) && $schedule['fri']) {
                            $criteria->setFriCheck(true);
                            $criteria->setFriTime(new \DateTime($schedule['outwardTime']));
                        }
                        if (isset($schedule['sat']) && $schedule['sat']) {
                            $criteria->setSatCheck(true);
                            $criteria->setSatTime(new \DateTime($schedule['outwardTime']));
                        }
                        if (isset($schedule['sun']) && $schedule['sun']) {
                            $criteria->setSunCheck(true);
                            $criteria->setSunTime(new \DateTime($schedule['outwardTime']));
                        }
                    }
                }
            }
        } elseif ($ad->getOutwardTime()) {
            $criteria->setFromTime(new \DateTime($ad->getOutwardTime()));
        }

        $ask->setCriteria($criteria);

        // we use the matching waypoints
        $waypoints = $matching->getWaypoints();
        foreach ($waypoints as $waypoint) {
            $newWaypoint = clone $waypoint;
            $ask->addWaypoint($newWaypoint);
        }

        // Ask History
        $askHistory = new AskHistory();
        $askHistory->setStatus($ask->getStatus());
        $askHistory->setType($ask->getType());
        $ask->addAskHistory($askHistory);

        // opposite ask ?
        if (Ad::ROLE_DRIVER_OR_PASSENGER == $ad->getRole() && $matching->getMatchingOpposite()) {
            // no role has been defined, we create the opposite ask
            $askOpposite = new Ask();
            $criteriaOpposite = clone $matching->getMatchingOpposite()->getCriteria();

            if ($ad->getAdId() == $matching->getMatchingOpposite()->getProposalOffer()->getId()) {
                // the carpooler is the driver, the requester is the passenger
                $askOpposite->setType($matching->getMatchingOpposite()->getProposalRequest()->getType());
                $askOpposite->setUser($matching->getMatchingOpposite()->getProposalRequest()->getUser());
            } else {
                // the carpooler is the passenger, the requester is the driver
                $askOpposite->setType($matching->getMatchingOpposite()->getProposalOffer()->getType());
                $askOpposite->setUser($matching->getMatchingOpposite()->getProposalOffer()->getUser());
            }

            $askOpposite->setStatus($ask->getStatus());
            $askOpposite->setMatching($matching->getMatchingOpposite());

            // for regular trips we need to check the dates and days
            if (Criteria::FREQUENCY_REGULAR == $matching->getCriteria()->getFrequency()) {
                if ($ad->getOutwardDate()) {
                    $criteriaOpposite->setFromDate($ad->getOutwardDate());
                }
                if ($ad->getOutwardLimitDate()) {
                    $criteriaOpposite->setToDate($ad->getOutwardLimitDate());
                }
                // we init the original schedule
                $criteriaOpposite->setMonCheck(false);
                $criteriaOpposite->setTueCheck(false);
                $criteriaOpposite->setWedCheck(false);
                $criteriaOpposite->setThuCheck(false);
                $criteriaOpposite->setFriCheck(false);
                $criteriaOpposite->setSatCheck(false);
                $criteriaOpposite->setSunCheck(false);
                // we use the driver times (the passenger times will be computed using these times and the chosen days)
                $criteriaOpposite->setMonTime($matching->getMatchingOpposite()->getProposalOffer()->getCriteria()->getMonTime());
                $criteriaOpposite->setTueTime($matching->getMatchingOpposite()->getProposalOffer()->getCriteria()->getTueTime());
                $criteriaOpposite->setWedTime($matching->getMatchingOpposite()->getProposalOffer()->getCriteria()->getWedTime());
                $criteriaOpposite->setThuTime($matching->getMatchingOpposite()->getProposalOffer()->getCriteria()->getThuTime());
                $criteriaOpposite->setFriTime($matching->getMatchingOpposite()->getProposalOffer()->getCriteria()->getFriTime());
                $criteriaOpposite->setSatTime($matching->getMatchingOpposite()->getProposalOffer()->getCriteria()->getSatTime());
                $criteriaOpposite->setSunTime($matching->getMatchingOpposite()->getProposalOffer()->getCriteria()->getSunTime());
            }

            $askOpposite->setCriteria($criteriaOpposite);

            // we use the matching waypoints
            $waypoints = $matching->getMatchingOpposite()->getWaypoints();
            foreach ($waypoints as $waypoint) {
                $newWaypoint = clone $waypoint;
                $askOpposite->addWaypoint($newWaypoint);
            }

            $ask->setAskOpposite($askOpposite);
        }

        // we treat the return (only for regular trips for now)
        if (Criteria::FREQUENCY_REGULAR == $matching->getCriteria()->getFrequency() && $matching->getMatchingLinked()) {
            $askReturn = new Ask();
            $criteriaReturn = clone $matching->getMatchingLinked()->getCriteria();

            $askReturn->setType(Proposal::TYPE_RETURN);
            $askReturn->setUser($ask->getUser());
            $askReturn->setStatus($ask->getStatus());
            $askReturn->setMatching($matching->getMatchingLinked());

            if ($ad->getOutwardDate()) {
                $criteriaReturn->setFromDate($ad->getOutwardDate());
            }
            if ($ad->getOutwardLimitDate()) {
                $criteriaReturn->setToDate($ad->getOutwardLimitDate());
            }

            // we init the original schedule
            $criteriaReturn->setMonCheck(false);
            $criteriaReturn->setTueCheck(false);
            $criteriaReturn->setWedCheck(false);
            $criteriaReturn->setThuCheck(false);
            $criteriaReturn->setFriCheck(false);
            $criteriaReturn->setSatCheck(false);
            $criteriaReturn->setSunCheck(false);
            // we use the driver times (the passenger times will be computed using these times and the chosen days)
            $criteriaReturn->setMonTime($matching->getMatchingLinked()->getProposalOffer()->getCriteria()->getMonTime());
            $criteriaReturn->setTueTime($matching->getMatchingLinked()->getProposalOffer()->getCriteria()->getTueTime());
            $criteriaReturn->setWedTime($matching->getMatchingLinked()->getProposalOffer()->getCriteria()->getWedTime());
            $criteriaReturn->setThuTime($matching->getMatchingLinked()->getProposalOffer()->getCriteria()->getThuTime());
            $criteriaReturn->setFriTime($matching->getMatchingLinked()->getProposalOffer()->getCriteria()->getFriTime());
            $criteriaReturn->setSatTime($matching->getMatchingLinked()->getProposalOffer()->getCriteria()->getSatTime());
            $criteriaReturn->setSunTime($matching->getMatchingLinked()->getProposalOffer()->getCriteria()->getSunTime());

            // we fill the selected days
            if (Ad::ROLE_DRIVER_OR_PASSENGER != $ad->getRole()) {
                foreach ($ad->getSchedule() as $schedule) {
                    if (isset($schedule['returnTime']) && '' != $schedule['returnTime']) {
                        if (isset($schedule['mon']) && $schedule['mon']) {
                            $criteriaReturn->setMonCheck(true);
                            $criteriaReturn->setMonTime(new \DateTime($schedule['returnTime']));
                        }
                        if (isset($schedule['tue']) && $schedule['tue']) {
                            $criteriaReturn->setTueCheck(true);
                            $criteriaReturn->setTueTime(new \DateTime($schedule['returnTime']));
                        }
                        if (isset($schedule['wed']) && $schedule['wed']) {
                            $criteriaReturn->setWedCheck(true);
                            $criteriaReturn->setWedTime(new \DateTime($schedule['returnTime']));
                        }
                        if (isset($schedule['thu']) && $schedule['thu']) {
                            $criteriaReturn->setThuCheck(true);
                            $criteriaReturn->setThuTime(new \DateTime($schedule['returnTime']));
                        }
                        if (isset($schedule['fri']) && $schedule['fri']) {
                            $criteriaReturn->setFriCheck(true);
                            $criteriaReturn->setFriTime(new \DateTime($schedule['returnTime']));
                        }
                        if (isset($schedule['sat']) && $schedule['sat']) {
                            $criteriaReturn->setSatCheck(true);
                            $criteriaReturn->setSatTime(new \DateTime($schedule['returnTime']));
                        }
                        if (isset($schedule['sun']) && $schedule['sun']) {
                            $criteriaReturn->setSunCheck(true);
                            $criteriaReturn->setSunTime(new \DateTime($schedule['returnTime']));
                        }
                    }
                }
            }

            $askReturn->setCriteria($criteriaReturn);

            // we use the matching waypoints
            $waypoints = $matching->getMatchingLinked()->getWaypoints();
            foreach ($waypoints as $waypoint) {
                $newWaypoint = clone $waypoint;
                $askReturn->addWaypoint($newWaypoint);
            }

            // opposite return ask ?
            if (Ad::ROLE_DRIVER_OR_PASSENGER == $ad->getRole() && $matching->getMatchingLinked()->getMatchingOpposite()) {
                // no role has been defined, we create the opposite ask
                $askReturnOpposite = new Ask();
                $criteriaReturnOpposite = clone $matching->getMatchingLinked()->getMatchingOpposite()->getCriteria();

                if ($ad->getAdId() == $matching->getMatchingLinked()->getMatchingOpposite()->getProposalOffer()->getId()) {
                    // the carpooler is the driver, the requester is the passenger
                    $askReturnOpposite->setType($matching->getMatchingLinked()->getMatchingOpposite()->getProposalRequest()->getType());
                    $askReturnOpposite->setUser($matching->getMatchingLinked()->getMatchingOpposite()->getProposalRequest()->getUser());
                } else {
                    // the carpooler is the passenger, the requester is the driver
                    $askReturnOpposite->setType($matching->getMatchingLinked()->getMatchingOpposite()->getProposalOffer()->getType());
                    $askReturnOpposite->setUser($matching->getMatchingLinked()->getMatchingOpposite()->getProposalOffer()->getUser());
                }

                $askReturnOpposite->setStatus($ask->getStatus());
                $askReturnOpposite->setMatching($matching->getMatchingLinked()->getMatchingOpposite());

                // for regular trips we need to check the dates and days
                if ($ad->getOutwardDate()) {
                    $criteriaReturnOpposite->setFromDate($ad->getOutwardDate());
                }
                if ($ad->getOutwardLimitDate()) {
                    $criteriaReturnOpposite->setToDate($ad->getOutwardLimitDate());
                }

                // we init the original schedule
                $criteriaReturnOpposite->setMonCheck(false);
                $criteriaReturnOpposite->setTueCheck(false);
                $criteriaReturnOpposite->setWedCheck(false);
                $criteriaReturnOpposite->setThuCheck(false);
                $criteriaReturnOpposite->setFriCheck(false);
                $criteriaReturnOpposite->setSatCheck(false);
                $criteriaReturnOpposite->setSunCheck(false);
                // we use the driver times (the passenger times will be computed using these times and the chosen days)
                $criteriaReturnOpposite->setMonTime($matching->getMatchingLinked()->getMatchingOpposite()->getProposalOffer()->getCriteria()->getMonTime());
                $criteriaReturnOpposite->setTueTime($matching->getMatchingLinked()->getMatchingOpposite()->getProposalOffer()->getCriteria()->getTueTime());
                $criteriaReturnOpposite->setWedTime($matching->getMatchingLinked()->getMatchingOpposite()->getProposalOffer()->getCriteria()->getWedTime());
                $criteriaReturnOpposite->setThuTime($matching->getMatchingLinked()->getMatchingOpposite()->getProposalOffer()->getCriteria()->getThuTime());
                $criteriaReturnOpposite->setFriTime($matching->getMatchingLinked()->getMatchingOpposite()->getProposalOffer()->getCriteria()->getFriTime());
                $criteriaReturnOpposite->setSatTime($matching->getMatchingLinked()->getMatchingOpposite()->getProposalOffer()->getCriteria()->getSatTime());
                $criteriaReturnOpposite->setSunTime($matching->getMatchingLinked()->getMatchingOpposite()->getProposalOffer()->getCriteria()->getSunTime());

                $askReturnOpposite->setCriteria($criteriaReturnOpposite);

                // we use the matching waypoints
                $waypoints = $matching->getMatchingLinked()->getMatchingOpposite()->getWaypoints();
                foreach ($waypoints as $waypoint) {
                    $newWaypoint = clone $waypoint;
                    $askReturnOpposite->addWaypoint($newWaypoint);
                }

                $askReturn->setAskOpposite($askReturnOpposite);
                if (isset($askOpposite)) {
                    $askReturnOpposite->setAskLinked($askOpposite);
                }
            }
            $ask->setAskLinked($askReturn);
        }

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

        if (Ask::STATUS_PENDING_AS_DRIVER == $ask->getStatus() || Ask::STATUS_PENDING_AS_PASSENGER == $ask->getStatus()) {
            // dispatch en event
            // get the complete ad to have data for the email
            $ad = $this->getAskFromAd($ask->getId(), $ask->getUser()->getId());
            $event = new AskPostedEvent($ad);
            $this->eventDispatcher->dispatch(AskPostedEvent::NAME, $event);
        }

        $ad->setAskId($ask->getId());

        // we create a message system
        if ($ad->getAskStatus() > Ask::STATUS_INITIATED) {
            $this->internalMessageManager->postAskRelatedMessageSystem($ask);
        }

        return $ad;
    }

    /**
     * Get an ask from an ad.
     *
     * @param int $askId  The ask id
     * @param int $userId The user id of the user making the request
     *
     * @return Ad The ad for the ask with the computed results
     */
    public function getAskFromAd(int $askId, int $userId)
    {
        $ask = $this->askRepository->find($askId);
        $ad = new Ad();
        $ad->setUserId($userId);
        $ad->setAskId($askId);
        $ad->setAskStatus($ask->getStatus());
        $ad->setMatchingId($ask->getMatching()->getId());
        $ad->setFrequency($ask->getMatching()->getCriteria()->getFrequency());

        // first pass for role
        switch ($ask->getStatus()) {
            case Ask::STATUS_INITIATED:
                if ($ask->getMatching()->getProposalOffer()->getUser()->getId() == $userId) {
                    $ad->setRole(Ad::ROLE_DRIVER);
                } else {
                    $ad->setRole(Ad::ROLE_PASSENGER);
                }

                break;

            case Ask::STATUS_PENDING_AS_DRIVER:
            case Ask::STATUS_ACCEPTED_AS_DRIVER:
            case Ask::STATUS_DECLINED_AS_DRIVER:
                $ad->setRole($ask->getUser()->getId() == $userId ? Ad::ROLE_DRIVER : Ad::ROLE_PASSENGER);

                break;

            case Ask::STATUS_PENDING_AS_PASSENGER:
            case Ask::STATUS_ACCEPTED_AS_PASSENGER:
            case Ask::STATUS_DECLINED_AS_PASSENGER:
                $ad->setRole($ask->getUser()->getId() == $userId ? Ad::ROLE_PASSENGER : Ad::ROLE_DRIVER);

                break;
        }

        // second pass for 'update-able'
        switch ($ask->getStatus()) {
            case Ask::STATUS_INITIATED:
                if ($ask->getUser()->getId() == $userId) {
                    $ad->setCanUpdateAsk(true);
                } else {
                    $ad->setCanUpdateAsk(false);
                }

                break;

            case Ask::STATUS_PENDING_AS_DRIVER:
            case Ask::STATUS_PENDING_AS_PASSENGER:
                if ($ask->getUser()->getId() == $userId) {
                    $ad->setCanUpdateAsk(false);
                } else {
                    $ad->setCanUpdateAsk(true);
                }

                break;

            default:
                $ad->setCanUpdateAsk(false);

                break;
        }

        // we compute the results
        $ad->setResults([$this->resultManager->createAskResults($ask, $userId)]);

        return $ad;
    }

    /**
     * Update an ask from an ad.
     *
     * @param Ad  $ad     The body of the ad to use
     * @param int $adId   The id of the ad to use (not initialized in the body)
     * @param int $userId The user id of the user making the update
     *
     * @return Ad The ad updated from the updated ask
     */
    public function updateAskFromAd(Ad $ad, int $adId, int $userId)
    {
        $ask = $this->askRepository->find($adId);

        // We check if the two Users in the Ask are involved in a block
        if ($this->blockManager->getInvolvedInABlock($ask->getUser(), $ask->getUserRelated())) {
            throw new BlockException(BlockException::MESSAGE_INVOLVED_IN_BLOCK);
        }

        // the ask posted is the master ask, we have to update all the asks linked :
        // - the related ask for return trip
        // - the opposite and return opposite if the role wasn't chosen (WE DON'T DO THAT ANYMORE)
        $ad->setRole($ask->getUser()->getId() == $userId ? Ad::ROLE_DRIVER : Ad::ROLE_PASSENGER);
        $ask->setStatus($ad->getAskStatus());
        if ($ask->getAskLinked()) {
            $ask->getAskLinked()->setStatus($ad->getAskStatus());
        }

        // UNCOMMENT TO UPDATE ALSO THE ASK OPPOSITE
        // if ($ask->getAskOpposite()) {
        //     $ask->getAskOpposite()->setStatus($ad->getAskStatus());
        //     if ($ask->getAskOpposite()->getAskLinked()) {
        //         $ask->getAskOpposite()->getAskLinked()->setStatus($ad->getAskStatus());
        //     }
        // }

        if ($ad->getOutwardDate() && $ad->getOutwardLimitDate() && count($ad->getSchedule()) > 0) {
            // regular
            // we update the criteria of the master ask
            $ask->getCriteria()->setFromDate($ad->getOutwardDate());
            $ask->getCriteria()->setToDate($ad->getOutwardLimitDate());
            // we init the original schedule
            $ask->getCriteria()->setMonCheck(false);
            $ask->getCriteria()->setTueCheck(false);
            $ask->getCriteria()->setWedCheck(false);
            $ask->getCriteria()->setThuCheck(false);
            $ask->getCriteria()->setFriCheck(false);
            $ask->getCriteria()->setSatCheck(false);
            $ask->getCriteria()->setSunCheck(false);
            if ($ask->getAskLinked()) {
                $ask->getAskLinked()->getCriteria()->setMonCheck(false);
                $ask->getAskLinked()->getCriteria()->setTueCheck(false);
                $ask->getAskLinked()->getCriteria()->setWedCheck(false);
                $ask->getAskLinked()->getCriteria()->setThuCheck(false);
                $ask->getAskLinked()->getCriteria()->setFriCheck(false);
                $ask->getAskLinked()->getCriteria()->setSatCheck(false);
                $ask->getAskLinked()->getCriteria()->setSunCheck(false);
                $ask->getAskLinked()->getCriteria()->setFromDate($ad->getOutwardDate());
                $ask->getAskLinked()->getCriteria()->setToDate($ad->getOutwardLimitDate());
            }
            foreach ($ad->getSchedule() as $schedule) {
                if (isset($schedule['outwardTime']) && '' != $schedule['outwardTime']) {
                    if (isset($schedule['mon']) && $schedule['mon']) {
                        $ask->getCriteria()->setMonCheck(true);
                    }
                    if (isset($schedule['tue']) && $schedule['tue']) {
                        $ask->getCriteria()->setTueCheck(true);
                    }
                    if (isset($schedule['wed']) && $schedule['wed']) {
                        $ask->getCriteria()->setWedCheck(true);
                    }
                    if (isset($schedule['thu']) && $schedule['thu']) {
                        $ask->getCriteria()->setThuCheck(true);
                    }
                    if (isset($schedule['fri']) && $schedule['fri']) {
                        $ask->getCriteria()->setFriCheck(true);
                    }
                    if (isset($schedule['sat']) && $schedule['sat']) {
                        $ask->getCriteria()->setSatCheck(true);
                    }
                    if (isset($schedule['sun']) && $schedule['sun']) {
                        $ask->getCriteria()->setSunCheck(true);
                    }
                }
                if ($ask->getAskLinked() && isset($schedule['returnTime']) && '' != $schedule['returnTime']) {
                    if (isset($schedule['mon']) && $schedule['mon']) {
                        $ask->getAskLinked()->getCriteria()->setMonCheck(true);
                    }
                    if (isset($schedule['tue']) && $schedule['tue']) {
                        $ask->getAskLinked()->getCriteria()->setTueCheck(true);
                    }
                    if (isset($schedule['wed']) && $schedule['wed']) {
                        $ask->getAskLinked()->getCriteria()->setWedCheck(true);
                    }
                    if (isset($schedule['thu']) && $schedule['thu']) {
                        $ask->getAskLinked()->getCriteria()->setThuCheck(true);
                    }
                    if (isset($schedule['fri']) && $schedule['fri']) {
                        $ask->getAskLinked()->getCriteria()->setFriCheck(true);
                    }
                    if (isset($schedule['sat']) && $schedule['sat']) {
                        $ask->getAskLinked()->getCriteria()->setSatCheck(true);
                    }
                    if (isset($schedule['sun']) && $schedule['sun']) {
                        $ask->getAskLinked()->getCriteria()->setSunCheck(true);
                    }
                }
            }
        }

        // Ask History
        $askHistory = new AskHistory();
        $askHistory->setStatus($ask->getStatus());
        $askHistory->setType($ask->getType());
        $ask->addAskHistory($askHistory);

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

        // If there is a SolidaryAsk we update it
        if (!is_null($ask->getSolidaryAsk())) {
            $solidaryAsk = $ask->getSolidaryAsk();
            if (Ask::STATUS_ACCEPTED_AS_DRIVER == $ad->getAskStatus() || Ask::STATUS_ACCEPTED_AS_PASSENGER == $ad->getAskStatus()) {
                $solidaryAsk->setStatus(SolidaryAsk::STATUS_ACCEPTED);
            } elseif (Ask::STATUS_DECLINED_AS_DRIVER == $ad->getAskStatus() || Ask::STATUS_DECLINED_AS_PASSENGER == $ad->getAskStatus()) {
                $solidaryAsk->setStatus(SolidaryAsk::STATUS_REFUSED);
            }

            // We clone the updated Criteria of the Ask
            $solidaryAsk->setCriteria(clone $ask->getCriteria());

            $solidaryAskHistory = new SolidaryAskHistory();
            $solidaryAskHistory->setSolidaryAsk($solidaryAsk);
            $solidaryAskHistory->setStatus($solidaryAsk->getStatus());

            $solidaryAsk->addSolidaryAskHistory($solidaryAskHistory);
            $this->entityManager->persist($solidaryAsk);
        }

        $this->entityManager->flush();

        // get the complete ad to have data for the email
        $ad = $this->getAskFromAd($ask->getId(), $userId);
        // dispatch en event
        if ((Ask::STATUS_PENDING_AS_DRIVER == $ask->getStatus()) || (Ask::STATUS_PENDING_AS_PASSENGER == $ask->getStatus())) {
            $event = new AskPostedEvent($ad);
            $this->eventDispatcher->dispatch(AskPostedEvent::NAME, $event);
        } elseif ((Ask::STATUS_ACCEPTED_AS_DRIVER == $ask->getStatus()) || (Ask::STATUS_ACCEPTED_AS_PASSENGER == $ask->getStatus())) {
            $event = new AskAcceptedEvent($ad);
            $this->eventDispatcher->dispatch(AskAcceptedEvent::NAME, $event);

            //  we dispatch gamification event associated
            $action = $this->actionRepository->findOneBy(['name' => 'carpool_ask_accepted']);
            $actionEvent = new ActionEvent($action, $ask->getUserRelated());
            $actionEvent->setAsk($ask);
            $this->eventDispatcher->dispatch($actionEvent, ActionEvent::NAME);
        } elseif ((Ask::STATUS_DECLINED_AS_DRIVER == $ask->getStatus()) || (Ask::STATUS_DECLINED_AS_PASSENGER == $ask->getStatus())) {
            $event = new AskRefusedEvent($ad);
            $this->eventDispatcher->dispatch(AskRefusedEvent::NAME, $event);
        }

        // we create a message system
        $this->internalMessageManager->postAskRelatedMessageSystem($ask);

        return $ad;
    }

    public function getAsksFromProposal(Proposal $proposal)
    {
        $asks = [];

        if (!empty($proposal->getMatchingOffers())) {
            $offers = $proposal->getMatchingOffers();

            /** @var Matching $offer */
            foreach ($offers as $offer) {
                if (!empty($offer->getAsks())) {
                    $asks = array_merge($asks, $offer->getAsks());
                }
            }
        }

        if (!empty($proposal->getMatchingRequests())) {
            $requests = $proposal->getMatchingRequests();

            /** @var Matching $request */
            foreach ($requests as $request) {
                if (!empty($request->getAsks())) {
                    $asks = array_merge($asks, $request->getAsks());
                }
            }
        }

        return $asks;
    }

    /**
     * Ask user is considered driver if he has made a proposal offer.
     *
     * @return bool
     */
    public function isAskUserDriver(Ask $ask)
    {
        return $ask->getUser()->getId() === $ask->getMatching()->getProposalOffer()->getUser()->getId();
    }

    /**
     * Ask user is considered passenger if he has made a proposal request.
     *
     * @return bool
     */
    public function isAskUserPassenger(Ask $ask)
    {
        return $ask->getUser()->getId() === $ask->getMatching()->getProposalRequest()->getUser()->getId();
    }

    /**
     * Get a simplified ask from an ad.
     *
     * @param int           $askId    The ask id
     * @param int           $userId   The user id of the user making the request
     * @param null|Proposal $proposal - We can give a Proposal if we need these data in results,
     *                                for example if my Ad is based on an ask and I need the proposal data in results
     *
     * @return Ad The ad for the ask with the computed results
     */
    public function getSimpleAskFromAd(int $askId, int $userId, ?Proposal $proposal = null)
    {
        $ask = $this->askRepository->find($askId);
        $ad = new Ad();
        $ad->setUserId($userId);
        $ad->setAskId($askId);
        $ad->setAskStatus($ask->getStatus());
        $ad->setOutwardLimitDate($ask->getCriteria()->getToDate());
        $ad->setFrequency($ask->getCriteria()->getFrequency());

        // If payment active we retreive the payement status of this ask
        if ($this->paymentActive) {
            $askWithPaymentStatus = $this->getPaymentStatus($askId);
            $ad->setPaymentStatus($askWithPaymentStatus->getPaymentStatus());
            $ad->setPaymentItemId($askWithPaymentStatus->getPaymentItemId());
            $ad->setUnpaidDate($askWithPaymentStatus->getUnpaidDate());
            $ad->setPaymentItemWeek($askWithPaymentStatus->getPaymentItemWeek());
        }

        // get the current proof id if relevant
        if ($carpoolProof = $this->carpoolProofRepository->findByAskAndDate($ask, new \DateTime())) {
            $ad->setCarpoolProofId($carpoolProof->getId());
        }

        // first pass for role
        switch ($ask->getStatus()) {
            case Ask::STATUS_ACCEPTED_AS_DRIVER:
                $ad->setRole($ask->getUser()->getId() == $userId ? Ad::ROLE_DRIVER : Ad::ROLE_PASSENGER);

                break;

            case Ask::STATUS_ACCEPTED_AS_PASSENGER:
                $ad->setRole($ask->getUser()->getId() == $userId ? Ad::ROLE_PASSENGER : Ad::ROLE_DRIVER);

                break;
        }
        // we compute the results
        if ($proposal) {
            $results = array_merge([$this->resultManager->createSimpleAskResults($ask, $userId, $ad->getRole())], $this->resultManager->createAdResults($proposal));
            $ad->setResults($results);
        } else {
            $ad->setResults([$this->resultManager->createSimpleAskResults($ask, $userId, $ad->getRole())]);
        }

        return $ad;
    }

    /**
     * Get the payment status of an Ask.
     *
     * @param int  $id   Id of the Ask to check
     * @param User $user The User we ask for (if null, it's the security token User)
     */
    public function getPaymentStatus(int $id, User $user = null): Ask
    {
        // search the ask
        if (!$ask = $this->getAsk($id)) {
            throw new PaymentException(PaymentException::NO_ASK_FOUND);
        }

        $driver = $ask->getMatching()->getProposalOffer()->getUser();
        $passenger = $ask->getMatching()->getProposalRequest()->getUser();

        if (null == $user) {
            $user = $this->security->getUser();
        }

        // The User can't be an App and has to be one of the Ask actor
        if (!($user instanceof User) || ($driver->getId() !== $user->getId() && $passenger->getId() !== $user->getId())) {
            throw new PaymentException(PaymentException::INVALID_USER);
        }

        // NB : We ignore the returns. For now, it's not possible to pay (or at least validate) only the outwards without the returns.
        // So we only treating the outwards to evaluate the payment status.
        $carpoolItemId = null;
        if (Criteria::FREQUENCY_PUNCTUAL == $ask->getCriteria()->getFrequency()) {
            // Punctual journey, we just check if it's paid on this particular day
            $carpoolItem = $this->carpoolItemRepository->findByAskAndDate($ask, $ask->getCriteria()->getFromDate());
            if (is_null($carpoolItem)) {
                return $ask;
                // throw new PaymentException(PaymentException::NO_CARPOOL_ITEM);
            }

            // Init the payment status at pending
            $ask->setPaymentStatus(Ask::PAYMENT_STATUS_PENDING);

            // If the status is Unpaid, it's the same for driver (creditor) or passenger (debtor)
            if (!is_null($carpoolItem->getUnpaidDate())) {
                $ask->setPaymentStatus(Ask::PAYMENT_STATUS_UNPAID);
                $ask->setUnpaidDate($carpoolItem->getUnpaidDate());
            } else {
                if ($driver->getId() == $user->getId()) {
                    // Driver (creditor) point of vue
                    if (CarpoolItem::CREDITOR_STATUS_DIRECT == $carpoolItem->getCreditorStatus()) {
                        $ask->setPaymentStatus(Ask::PAYMENT_STATUS_DIRECT);
                    }
                    if (CarpoolItem::CREDITOR_STATUS_ONLINE == $carpoolItem->getCreditorStatus()) {
                        $ask->setPaymentStatus(Ask::PAYMENT_STATUS_ONLINE);
                    }
                } else {
                    // Passenger (debtor) point of vue
                    if (CarpoolItem::DEBTOR_STATUS_DIRECT == $carpoolItem->getDebtorStatus() || CarpoolItem::DEBTOR_STATUS_PENDING_DIRECT == $carpoolItem->getDebtorStatus()) {
                        $ask->setPaymentStatus(Ask::PAYMENT_STATUS_DIRECT);
                    }
                    if (CarpoolItem::DEBTOR_STATUS_ONLINE == $carpoolItem->getDebtorStatus()) {
                        $ask->setPaymentStatus(Ask::PAYMENT_STATUS_ONLINE);
                    }
                }
            }
            // Id of the CarpoolItem
            $carpoolItemId = $carpoolItem->getId();
        } else {
            $ask->setPaymentStatus(Ask::PAYMENT_STATUS_PAID);

            // Regular journey. To be paid, all the previous week must have been confirmed
            $askWithNonValidatedWeeks = $this->getNonValidatedWeeks($ask, $user);
            $nonValidatedWeeks = $askWithNonValidatedWeeks->getWeekItems();
            foreach ($nonValidatedWeeks as $nonValidatedWeek) {
                if (!is_null($nonValidatedWeek->getUnpaidDate())) {
                    $ask->setPaymentStatus(Ask::PAYMENT_STATUS_UNPAID);
                    $ask->setUnpaidDate($nonValidatedWeek->getUnpaidDate());
                    $carpoolItemId = $nonValidatedWeek->getPaymentItemId();
                    $ask->setPaymentItemWeek($nonValidatedWeeks[0]->getNumWeek().''.$nonValidatedWeeks[0]->getYear());

                    break;
                }
                if (WeekItem::STATUS_PENDING == $nonValidatedWeek->getStatus()) {
                    $ask->setPaymentStatus(Ask::PAYMENT_STATUS_PENDING);
                }
                $carpoolItemId = $nonValidatedWeek->getPaymentItemId();
                $ask->setPaymentItemWeek($nonValidatedWeeks[0]->getNumWeek().''.$nonValidatedWeeks[0]->getYear());
            }
        }

        // Id of the CarpoolItem
        $ask->setPaymentItemId($carpoolItemId);

        return $ask;
    }

    /**
     * Get the non validated weeks of an Ask.
     *
     * @param Ask $ask  Ask
     * @param Ask $user User that want to get the non validated weeks
     */
    public function getNonValidatedWeeks(Ask $ask, User $user): Ask
    {
        $startDate = $ask->getCriteria()->getFromDate();
        $toDate = $ask->getCriteria()->getToDate();

        // we limit to the last day of the previous week
        $maxDate = new \DateTime();
        $maxDate->modify('last week +6 days');

        $limitDate = min($maxDate, $toDate);

        // First we need an array where every element is a week that contains every days on the period
        $currentDate = clone $startDate;

        $arrayWeeks = [];
        while ($currentDate <= $limitDate) {
            $currentWeek = [];
            for ($i = 0; $i < 7; ++$i) {
                $currentWeek[] = clone $currentDate;
                $currentDate->modify('+1 day');
            }
            $arrayWeeks[] = $currentWeek;
        }

        // var_dump($arrayWeeks);die;

        // For each week we need to determine all day are confirmed. If not, the week is still in payement
        $nonValidatedWeeks = [];
        $firstCarpoolItem = null;
        $unpaidDate = null;
        foreach ($arrayWeeks as $currentWeek) {
            $validatedWeek = false;
            $unpaidDetected = false;
            foreach ($currentWeek as $currentDay) {
                $carpoolItem = $this->carpoolItemRepository->findByAskAndDate($ask, $currentDay);
                if (!is_null($carpoolItem)) {
                    if (is_null($firstCarpoolItem)) {
                        $firstCarpoolItem = $carpoolItem;
                    }
                    if (!is_null($carpoolItem->getUnpaidDate())) {
                        $unpaidDetected = true;
                        $unpaidDate = $carpoolItem->getUnpaidDate();

                        break;
                    }

                    // The validated status depends on the point of vue of the current user
                    if (CarpoolItem::STATUS_INITIALIZED !== $carpoolItem->getItemStatus()) {
                        if ($carpoolItem->getDebtorUser()->getId() == $user->getId()
                            && CarpoolItem::DEBTOR_STATUS_PENDING !== $carpoolItem->getDebtorStatus()
                        ) {
                            // The day has been confirmed by the debtor, the week is validated for him
                            $validatedWeek = true;

                            break;
                        }
                        if ($carpoolItem->getCreditorUser()->getId() == $user->getId()
                            && CarpoolItem::CREDITOR_STATUS_PENDING !== $carpoolItem->getCreditorStatus()
                        ) {
                            // The day has been confirmed by the creditor, the week is validated for him
                            $validatedWeek = true;

                            break;
                        }
                    }
                }
            }

            if ((!$validatedWeek || $unpaidDetected) && !is_null($firstCarpoolItem)) {
                $weekItem = new WeekItem();
                $weekItem->setFromDate($currentWeek[0]);
                $weekItem->setToDate($currentWeek[count($currentWeek) - 1]);
                $weekItem->setNumWeek($currentWeek[0]->format('W'));
                $weekItem->setYear($currentWeek[0]->format('Y'));
                $weekItem->setStatus(WeekItem::STATUS_PENDING);
                $weekItem->setPaymentItemId($firstCarpoolItem->getId());
                $weekItem->setUnpaidDate($unpaidDate);
                // if ($unpaidDetected) {
                //     $weekItem->setStatus(WeekItem::STATUS_UNPAID);
                // }
                $nonValidatedWeeks[] = $weekItem;
            }
        }

        $ask->setWeekItems($nonValidatedWeeks);

        return $ask;
    }

    // DYNAMIC

    /**
     * Check if a user has a pending dynamic ad ask.
     *
     * @param User $user The user
     *
     * @return bool
     */
    public function hasPendingDynamicAsk(User $user)
    {
        // first we get all the asks initiated by the user
        $asks = $this->askRepository->findBy(['user' => $user, 'status' => [Ask::STATUS_PENDING_AS_PASSENGER, Ask::STATUS_ACCEPTED_AS_DRIVER]]);
        // now we check if one of these asks is related to a dynamic ad, not finished
        foreach ($asks as $ask) {
            // if the user is passenger
            if ($ask->getUser()->getId() == $user->getId() && $ask->getMatching()->getProposalRequest()->isDynamic() && !$ask->getMatching()->getProposalRequest()->isFinished()) {
                return true;
            }
            // if the user is driver
            if ($ask->getUserRelated()->getId() == $user->getId() && $ask->getMatching()->getProposalOffer()->isDynamic() && !$ask->getMatching()->getProposalOffer()->isFinished()) {
                return true;
            }
        }

        return false;
    }

    /**
     * Check if a user has a refused dynamic ad ask related to a given matching.
     *
     * @param User $user The user
     *
     * @return bool
     */
    public function hasRefusedDynamicAsk(User $user, Matching $matching)
    {
        // first we get all the asks initiated by the user and refused by the carpooler
        $asks = $this->askRepository->findBy(['user' => $user, 'status' => [Ask::STATUS_DECLINED_AS_DRIVER]]);
        // now we check if one of these asks is related to the given matching
        foreach ($asks as $ask) {
            if ($ask->getMatching()->getId() == $matching->getId()) {
                return true;
            }
        }

        return false;
    }

    /**
     * Create the associated AskHistory of an Ask.
     */
    private function createAssociatedAskHistory(Ask $ask)
    {
        $askHistory = new AskHistory();

        $askHistory->setStatus($ask->getStatus());
        $askHistory->setType($ask->getType());
        $askHistory->setAsk($ask);

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

        return $askHistory;
    }
}