Covivo/mobicoop

View on GitHub
api/src/Solidary/Admin/Service/SolidaryManager.php

Summary

Maintainability
F
3 wks
Test Coverage
<?php

/**
 * Copyright (c) 2021, 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\Solidary\Admin\Service;

use ApiPlatform\Core\DataProvider\PaginatorInterface;
use App\Action\Entity\Action;
use App\Action\Entity\Animation;
use App\Action\Entity\Diary;
use App\Action\Event\AnimationMadeEvent;
use App\Action\Repository\ActionRepository;
use App\Auth\Entity\AuthItem;
use App\Auth\Entity\UserAuthAssignment;
use App\Auth\Repository\AuthItemRepository;
use App\Auth\Repository\UserAuthAssignmentRepository;
use App\Carpool\Entity\Criteria;
use App\Carpool\Entity\Matching;
use App\Carpool\Entity\Proposal;
use App\Carpool\Entity\Waypoint;
use App\Carpool\Repository\ProposalRepository;
use App\Carpool\Ressource\Ad;
use App\Carpool\Service\AdManager;
use App\Carpool\Service\ProposalMatcher;
use App\Communication\Entity\Message;
use App\Communication\Entity\Recipient;
use App\Communication\Repository\MessageRepository;
use App\Communication\Service\InternalMessageManager;
use App\Geography\Entity\Address;
use App\Service\FileManager;
use App\Solidary\Admin\Event\SolidaryCreatedEvent;
use App\Solidary\Admin\Event\SolidaryDeeplyUpdated;
use App\Solidary\Admin\Exception\SolidaryException;
use App\Solidary\Entity\Need;
use App\Solidary\Entity\Proof;
use App\Solidary\Entity\Solidary;
use App\Solidary\Entity\SolidaryAsk;
use App\Solidary\Entity\SolidaryAskHistory;
use App\Solidary\Entity\SolidaryMatching;
use App\Solidary\Entity\SolidarySolution;
use App\Solidary\Entity\SolidaryUser;
use App\Solidary\Entity\SolidaryUserStructure;
use App\Solidary\Entity\Structure;
use App\Solidary\Entity\Subject;
use App\Solidary\Repository\NeedRepository;
use App\Solidary\Repository\SolidaryMatchingRepository;
use App\Solidary\Repository\SolidaryRepository;
use App\Solidary\Repository\SolidarySolutionRepository;
use App\Solidary\Repository\SolidaryUserRepository;
use App\Solidary\Repository\SolidaryUserStructureRepository;
use App\Solidary\Repository\StructureProofRepository;
use App\Solidary\Repository\StructureRepository;
use App\Solidary\Repository\SubjectRepository;
use App\User\Admin\Service\UserManager;
use App\User\Entity\User;
use App\User\Repository\UserRepository;
use DateInterval;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\Security\Core\Security;

/**
 * Solidary manager in admin context.
 *
 * @author Sylvain Briat <sylvain.briat@mobicoop.org>
 */
class SolidaryManager
{
    /**
     * @var User
     */
    private $poster;
    private $entityManager;
    private $userManager;
    private $adManager;
    private $internalMessageManager;
    private $userRepository;
    private $structureProofRepository;
    private $solidaryUserRepository;
    private $structureRepository;
    private $solidaryUserStructureRepository;
    private $proposalRepository;
    private $authItemRepository;
    private $userAuthAssignmentRepository;
    private $subjectRepository;
    private $needRepository;
    private $eventDispatcher;
    private $solidaryRepository;
    private $actionRepository;
    private $solidaryMatchingRepository;
    private $solidarySolutionRepository;
    private $messageRepository;
    private $solidaryTransportMatcher;
    private $solidaryBeneficiaryManager;
    private $fileManager;
    private $proposalMatcher;
    private $logger;

    /**
     * Constructor.
     */
    public function __construct(
        EntityManagerInterface $entityManager,
        LoggerInterface $logger,
        Security $security,
        UserManager $userManager,
        AdManager $adManager,
        InternalMessageManager $internalMessageManager,
        UserRepository $userRepository,
        StructureProofRepository $structureProofRepository,
        SolidaryUserRepository $solidaryUserRepository,
        StructureRepository $structureRepository,
        SolidaryUserStructureRepository $solidaryUserStructureRepository,
        ProposalRepository $proposalRepository,
        AuthItemRepository $authItemRepository,
        UserAuthAssignmentRepository $userAuthAssignmentRepository,
        SubjectRepository $subjectRepository,
        NeedRepository $needRepository,
        EventDispatcherInterface $eventDispatcher,
        SolidaryRepository $solidaryRepository,
        ActionRepository $actionRepository,
        SolidaryMatchingRepository $solidaryMatchingRepository,
        SolidarySolutionRepository $solidarySolutionRepository,
        MessageRepository $messageRepository,
        SolidaryTransportMatcher $solidaryTransportMatcher,
        SolidaryBeneficiaryManager $solidaryBeneficiaryManager,
        FileManager $fileManager,
        ProposalMatcher $proposalMatcher
    ) {
        $this->logger = $logger;
        $this->poster = $security->getUser();
        $this->entityManager = $entityManager;
        $this->userManager = $userManager;
        $this->adManager = $adManager;
        $this->internalMessageManager = $internalMessageManager;
        $this->userRepository = $userRepository;
        $this->structureProofRepository = $structureProofRepository;
        $this->solidaryUserRepository = $solidaryUserRepository;
        $this->structureRepository = $structureRepository;
        $this->solidaryUserStructureRepository = $solidaryUserStructureRepository;
        $this->proposalRepository = $proposalRepository;
        $this->authItemRepository = $authItemRepository;
        $this->userAuthAssignmentRepository = $userAuthAssignmentRepository;
        $this->subjectRepository = $subjectRepository;
        $this->needRepository = $needRepository;
        $this->eventDispatcher = $eventDispatcher;
        $this->solidaryRepository = $solidaryRepository;
        $this->actionRepository = $actionRepository;
        $this->solidaryMatchingRepository = $solidaryMatchingRepository;
        $this->solidarySolutionRepository = $solidarySolutionRepository;
        $this->messageRepository = $messageRepository;
        $this->solidaryTransportMatcher = $solidaryTransportMatcher;
        $this->solidaryBeneficiaryManager = $solidaryBeneficiaryManager;
        $this->fileManager = $fileManager;
        $this->proposalMatcher = $proposalMatcher;
    }

    /**
     * Get a Solidary record.
     *
     * @param int $id The solidary id
     *
     * @return Solidary The solidary record
     */
    public function getSolidary(int $id): Solidary
    {
        $solidary = $this->solidaryRepository->find($id);

        // link potential outward and return solidaryMatchings that would have not been made yet (as the link between Matchings are made after the SolidaryMatchings)
        $this->solidaryMatchingRepository->linkRelatedSolidaryMatchings($solidary->getId());

        // check if the solidary record is the parent of another solidary record
        if ($child = $this->solidaryRepository->getChild($solidary)) {
            $solidary->setAdminSolidaryChildId($child->getId());
        }

        // create schedules
        $schedules = [];
        $days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
        foreach ($days as $num => $day) {
            $this->treatDay($solidary->getProposal(), $num, $day, $schedules);
        }
        $solidary->setAdminschedules($schedules);

        if (Criteria::FREQUENCY_FLEXIBLE == $solidary->getAdminfrequency()) {
            // set min and max time for flexible proposal
            // for a flexible proposal, we have only one schedule, with all days checked
            $solidary->setAdminoutwardMinTime($solidary->getProposal()->getCriteria()->getMonMinTime());
            $solidary->setAdminoutwardTime($solidary->getProposal()->getCriteria()->getMonTime());
            $solidary->setAdminoutwardMaxTime($solidary->getProposal()->getCriteria()->getMonMaxTime());
            if ($solidary->getProposal()->getProposalLinked()) {
                $solidary->setAdminreturnMinTime($solidary->getProposal()->getProposalLinked()->getCriteria()->getMonMinTime());
                $solidary->setAdminreturnTime($solidary->getProposal()->getProposalLinked()->getCriteria()->getMonTime());
                $solidary->setAdminreturnMaxTime($solidary->getProposal()->getProposalLinked()->getCriteria()->getMonMaxTime());
            }
        } elseif (Criteria::FREQUENCY_PUNCTUAL == $solidary->getAdminfrequency()) {
            // set time for punctual proposal
            $solidary->setAdminoutwardMinTime($solidary->getProposal()->getCriteria()->getMinTime());
            $solidary->setAdminoutwardTime($solidary->getProposal()->getCriteria()->getFromTime());
            $solidary->setAdminoutwardMaxTime($solidary->getProposal()->getCriteria()->getMaxTime());
            if ($solidary->getProposal()->getProposalLinked()) {
                $solidary->setAdminreturnMinTime($solidary->getProposal()->getProposalLinked()->getCriteria()->getMinTime());
                $solidary->setAdminreturnTime($solidary->getProposal()->getProposalLinked()->getCriteria()->getFromTime());
                $solidary->setAdminreturnMaxTime($solidary->getProposal()->getProposalLinked()->getCriteria()->getMaxTime());
            }
        }

        // set operator informations
        $diaries = $this->solidaryRepository->getDiaries($solidary);
        if (count($diaries) > 0) {
            foreach ($diaries as $diary) {
                /**
                 * @var Diary $diary
                 */
                if (Action::SOLIDARY_CREATE === $diary->getAction()->getId() && $diary->getAuthor()->getId() !== $diary->getUser()->getId()) {
                    $solidary->setAdminoperatorGivenName($diary->getAuthor()->getGivenName());
                    $solidary->setAdminoperatorFamilyName($diary->getAuthor()->getFamilyName());
                    $solidary->setAdminoperatorAvatar($diary->getAuthor()->getAvatar());
                }
            }
        }

        // set proofs
        $solidary->setAdminproofs($this->solidaryBeneficiaryManager->getProofsForSolidaryUserStructure($solidary->getSolidaryUserStructure(), $solidary->getSolidaryUserStructure()->getStructure()));

        // set carpools and transporters
        $carpools = [
            'outward' => [],
            'return' => [],
        ];
        $volunteers = [
            'outward' => [],
            'return' => [],
        ];
        foreach ($solidary->getSolidaryMatchings() as $solidaryMatching) {
            /**
             * @var SolidaryMatching $solidaryMatching
             */
            if ($solidaryMatching->getMatching()) {
                // carpool matching
                $carpool = [
                    'external' => false,
                    'externalProvider' => null,
                    'journeyId' => null,
                    'matchingId' => $solidaryMatching->getId(),
                    'carpoolerId' => $solidaryMatching->getMatching()->getProposalOffer()->getUser()->getId(),
                    'carpoolerGivenName' => $solidaryMatching->getMatching()->getProposalOffer()->getUser()->getGivenName(),
                    'carpoolerFamilyName' => $solidaryMatching->getMatching()->getProposalOffer()->getUser()->getFamilyName(),
                    'carpoolerAvatar' => $solidaryMatching->getMatching()->getProposalOffer()->getUser()->getAvatar(),
                    'frequency' => $solidaryMatching->getMatching()->getProposalOffer()->getCriteria()->getFrequency(),
                    // type is used to determine if the carpool has only an outward or also a return
                    'type' => $solidaryMatching->getType() ? (Proposal::TYPE_ONE_WAY == $solidaryMatching->getType() ? 'oneway' : 'roundtrip') : (Proposal::TYPE_ONE_WAY == $solidaryMatching->getMatching()->getProposalOffer()->getType() ? 'oneway' : 'roundtrip'),
                    'passenger' => $solidaryMatching->getMatching()->getProposalOffer()->getCriteria()->isPassenger(),
                    'driver' => $solidaryMatching->getMatching()->getProposalOffer()->getCriteria()->isDriver(),
                    'solidaryExclusive' => $solidaryMatching->getMatching()->getProposalOffer()->getCriteria()->isSolidaryExclusive(),
                    'fromDate' => $solidaryMatching->getMatching()->getCriteria()->getFromDate(),
                    'fromTime' => $solidaryMatching->getMatching()->getCriteria()->getFromTime(),
                    'toDate' => $solidaryMatching->getMatching()->getCriteria()->getToDate(),
                    'carpoolerFromDate' => $solidaryMatching->getMatching()->getProposalOffer()->getCriteria()->getFromDate(),
                    'carpoolerFromTime' => $solidaryMatching->getMatching()->getProposalOffer()->getCriteria()->getFromTime(),
                    'carpoolerToDate' => $solidaryMatching->getMatching()->getProposalOffer()->getCriteria()->getToDate(),
                ];
                if (Criteria::FREQUENCY_REGULAR == $solidaryMatching->getMatching()->getProposalOffer()->getCriteria()->getFrequency()) {
                    $carpool['carpoolerSchedule'] = [
                        'mon' => $solidaryMatching->getMatching()->getProposalOffer()->getCriteria()->isMonCheck() ? $solidaryMatching->getMatching()->getProposalOffer()->getCriteria()->getMonTime() : false,
                        'tue' => $solidaryMatching->getMatching()->getProposalOffer()->getCriteria()->isTueCheck() ? $solidaryMatching->getMatching()->getProposalOffer()->getCriteria()->getTueTime() : false,
                        'wed' => $solidaryMatching->getMatching()->getProposalOffer()->getCriteria()->isWedCheck() ? $solidaryMatching->getMatching()->getProposalOffer()->getCriteria()->getWedTime() : false,
                        'thu' => $solidaryMatching->getMatching()->getProposalOffer()->getCriteria()->isThuCheck() ? $solidaryMatching->getMatching()->getProposalOffer()->getCriteria()->getThuTime() : false,
                        'fri' => $solidaryMatching->getMatching()->getProposalOffer()->getCriteria()->isFriCheck() ? $solidaryMatching->getMatching()->getProposalOffer()->getCriteria()->getFriTime() : false,
                        'sat' => $solidaryMatching->getMatching()->getProposalOffer()->getCriteria()->isSatCheck() ? $solidaryMatching->getMatching()->getProposalOffer()->getCriteria()->getSatTime() : false,
                        'sun' => $solidaryMatching->getMatching()->getProposalOffer()->getCriteria()->isSunCheck() ? $solidaryMatching->getMatching()->getProposalOffer()->getCriteria()->getSunTime() : false,
                    ];
                    $carpool['schedule'] = [
                        'mon' => $solidaryMatching->getMatching()->getCriteria()->isMonCheck() ? $solidaryMatching->getMatching()->getCriteria()->getMonTime() : false,
                        'tue' => $solidaryMatching->getMatching()->getCriteria()->isTueCheck() ? $solidaryMatching->getMatching()->getCriteria()->getTueTime() : false,
                        'wed' => $solidaryMatching->getMatching()->getCriteria()->isWedCheck() ? $solidaryMatching->getMatching()->getCriteria()->getWedTime() : false,
                        'thu' => $solidaryMatching->getMatching()->getCriteria()->isThuCheck() ? $solidaryMatching->getMatching()->getCriteria()->getThuTime() : false,
                        'fri' => $solidaryMatching->getMatching()->getCriteria()->isFriCheck() ? $solidaryMatching->getMatching()->getCriteria()->getFriTime() : false,
                        'sat' => $solidaryMatching->getMatching()->getCriteria()->isSatCheck() ? $solidaryMatching->getMatching()->getCriteria()->getSatTime() : false,
                        'sun' => $solidaryMatching->getMatching()->getCriteria()->isSunCheck() ? $solidaryMatching->getMatching()->getCriteria()->getSunTime() : false,
                    ];
                }
                foreach ($solidaryMatching->getMatching()->getProposalOffer()->getWaypoints() as $waypoint) {
                    /**
                     * @var Waypoint $waypoint
                     */
                    if (0 == $waypoint->getPosition()) {
                        $carpool['origin'] = $waypoint->getAddress()->jsonSerialize();
                        if (Criteria::FREQUENCY_PUNCTUAL == $solidaryMatching->getMatching()->getProposalOffer()->getCriteria()->getFrequency()) {
                            /**
                             * @var DateTime $destinationTime
                             */
                            $destinationTime = clone $solidaryMatching->getMatching()->getProposalOffer()->getCriteria()->getFromTime();
                            $destinationTime->add(new \DateInterval('PT'.$solidaryMatching->getMatching()->getOriginalDuration().'S'));
                            $carpool['destinationTime'] = $destinationTime;
                        }
                    }
                    if ($waypoint->isDestination()) {
                        $carpool['destination'] = $waypoint->getAddress()->jsonSerialize();
                    }
                    $carpool['detourDuration'] = $solidaryMatching->getMatching()->getDetourDuration();
                    $carpool['detourDistance'] = $solidaryMatching->getMatching()->getDetourDistance();
                }
                if (Proposal::TYPE_RETURN !== $solidaryMatching->getMatching()->getProposalRequest()->getType()) {
                    $carpools['outward'][] = $carpool;
                } else {
                    $carpools['return'][] = $carpool;
                }
            } else {
                // volunteer matching
                $volunteer = [
                    'matchingId' => $solidaryMatching->getId(),
                    'volunteerId' => $solidaryMatching->getSolidaryUser()->getUser()->getId(),
                    'volunteerGivenName' => $solidaryMatching->getSolidaryUser()->getUser()->getGivenName(),
                    'volunteerFamilyName' => $solidaryMatching->getSolidaryUser()->getUser()->getFamilyName(),
                    'volunteerAvatar' => $solidaryMatching->getSolidaryUser()->getUser()->getAvatar(),
                    'centerPoint' => $solidaryMatching->getSolidaryUser()->getAddress()->jsonSerialize(),
                    'maxDistance' => $solidaryMatching->getSolidaryUser()->getMaxDistance(),
                    // type is used to determine if the journey has only an outward or also a return
                    'type' => Proposal::TYPE_ONE_WAY == $solidaryMatching->getType() ? 'oneway' : 'roundtrip',
                    'mMinTime' => $solidaryMatching->getSolidaryUser()->getMMinTime(), 'mMaxTime' => $solidaryMatching->getSolidaryUser()->getMMaxTime(),
                    'aMinTime' => $solidaryMatching->getSolidaryUser()->getAMinTime(), 'aMaxTime' => $solidaryMatching->getSolidaryUser()->getAMaxTime(),
                    'eMinTime' => $solidaryMatching->getSolidaryUser()->getEMinTime(), 'eMaxTime' => $solidaryMatching->getSolidaryUser()->getEMaxTime(),
                ];
                // original schedule for the volunteer
                $volunteer['volunteerSchedule'] = [
                    'mMon' => $solidaryMatching->getSolidaryUser()->hasMMon(), 'aMon' => $solidaryMatching->getSolidaryUser()->hasAMon(), 'eMon' => $solidaryMatching->getSolidaryUser()->hasEMon(),
                    'mTue' => $solidaryMatching->getSolidaryUser()->hasMTue(), 'aTue' => $solidaryMatching->getSolidaryUser()->hasATue(), 'eTue' => $solidaryMatching->getSolidaryUser()->hasETue(),
                    'mWed' => $solidaryMatching->getSolidaryUser()->hasMWed(), 'aWed' => $solidaryMatching->getSolidaryUser()->hasAWed(), 'eWed' => $solidaryMatching->getSolidaryUser()->hasEWed(),
                    'mThu' => $solidaryMatching->getSolidaryUser()->hasMThu(), 'aThu' => $solidaryMatching->getSolidaryUser()->hasAThu(), 'eThu' => $solidaryMatching->getSolidaryUser()->hasEThu(),
                    'mFri' => $solidaryMatching->getSolidaryUser()->hasMFri(), 'aFri' => $solidaryMatching->getSolidaryUser()->hasAFri(), 'eFri' => $solidaryMatching->getSolidaryUser()->hasEFri(),
                    'mSat' => $solidaryMatching->getSolidaryUser()->hasMSat(), 'aSat' => $solidaryMatching->getSolidaryUser()->hasASat(), 'eSat' => $solidaryMatching->getSolidaryUser()->hasESat(),
                    'mSun' => $solidaryMatching->getSolidaryUser()->hasMSun(), 'aSun' => $solidaryMatching->getSolidaryUser()->hasASun(), 'eSun' => $solidaryMatching->getSolidaryUser()->hasESun(),
                ];
                // computed schedule for the volunteer regarding the matching criteria
                $volunteer['schedule'] = [
                    'mMon' => false, 'aMon' => false, 'eMon' => false,
                    'mTue' => false, 'aTue' => false, 'eTue' => false,
                    'mWed' => false, 'aWed' => false, 'eWed' => false,
                    'mThu' => false, 'aThu' => false, 'eThu' => false,
                    'mFri' => false, 'aFri' => false, 'eFri' => false,
                    'mSat' => false, 'aSat' => false, 'eSat' => false,
                    'mSun' => false, 'aSun' => false, 'eSun' => false,
                ];
                if (Criteria::FREQUENCY_REGULAR == $solidaryMatching->getCriteria()->getFrequency()) {
                    // regular schedule
                    if (
                        $solidaryMatching->getCriteria()->isMonCheck()
                        && strtotime($solidaryMatching->getCriteria()->getMonMinTime()->format('H:i:s')) < strtotime($solidaryMatching->getSolidaryUser()->getMMaxTime()->format('H:i:s'))
                        && strtotime($solidaryMatching->getCriteria()->getMonMaxTime()->format('H:i:s')) >= strtotime($solidaryMatching->getSolidaryUser()->getMMinTime()->format('H:i:s'))
                    ) {
                        $volunteer['schedule']['mMon'] = true;
                    }
                    if (
                        $solidaryMatching->getCriteria()->isMonCheck()
                        && strtotime($solidaryMatching->getCriteria()->getMonMinTime()->format('H:i:s')) < strtotime($solidaryMatching->getSolidaryUser()->getAMaxTime()->format('H:i:s'))
                        && strtotime($solidaryMatching->getCriteria()->getMonMaxTime()->format('H:i:s')) >= strtotime($solidaryMatching->getSolidaryUser()->getAMinTime()->format('H:i:s'))
                    ) {
                        $volunteer['schedule']['aMon'] = true;
                    }
                    if (
                        $solidaryMatching->getCriteria()->isMonCheck()
                        && strtotime($solidaryMatching->getCriteria()->getMonMinTime()->format('H:i:s')) < strtotime($solidaryMatching->getSolidaryUser()->getEMaxTime()->format('H:i:s'))
                        && strtotime($solidaryMatching->getCriteria()->getMonMaxTime()->format('H:i:s')) >= strtotime($solidaryMatching->getSolidaryUser()->getEMinTime()->format('H:i:s'))
                    ) {
                        $volunteer['schedule']['eMon'] = true;
                    }
                    if (
                        $solidaryMatching->getCriteria()->isTueCheck()
                        && strtotime($solidaryMatching->getCriteria()->getTueMinTime()->format('H:i:s')) < strtotime($solidaryMatching->getSolidaryUser()->getMMaxTime()->format('H:i:s'))
                        && strtotime($solidaryMatching->getCriteria()->getTueMaxTime()->format('H:i:s')) >= strtotime($solidaryMatching->getSolidaryUser()->getMMinTime()->format('H:i:s'))
                    ) {
                        $volunteer['schedule']['mTue'] = true;
                    }
                    if (
                        $solidaryMatching->getCriteria()->isTueCheck()
                        && strtotime($solidaryMatching->getCriteria()->getTueMinTime()->format('H:i:s')) < strtotime($solidaryMatching->getSolidaryUser()->getAMaxTime()->format('H:i:s'))
                        && strtotime($solidaryMatching->getCriteria()->getTueMaxTime()->format('H:i:s')) >= strtotime($solidaryMatching->getSolidaryUser()->getAMinTime()->format('H:i:s'))
                    ) {
                        $volunteer['schedule']['aTue'] = true;
                    }
                    if (
                        $solidaryMatching->getCriteria()->isTueCheck()
                        && strtotime($solidaryMatching->getCriteria()->getTueMinTime()->format('H:i:s')) < strtotime($solidaryMatching->getSolidaryUser()->getEMaxTime()->format('H:i:s'))
                        && strtotime($solidaryMatching->getCriteria()->getTueMaxTime()->format('H:i:s')) >= strtotime($solidaryMatching->getSolidaryUser()->getEMinTime()->format('H:i:s'))
                    ) {
                        $volunteer['schedule']['eTue'] = true;
                    }
                    if (
                        $solidaryMatching->getCriteria()->isWedCheck()
                        && strtotime($solidaryMatching->getCriteria()->getWedMinTime()->format('H:i:s')) < strtotime($solidaryMatching->getSolidaryUser()->getMMaxTime()->format('H:i:s'))
                        && strtotime($solidaryMatching->getCriteria()->getWedMaxTime()->format('H:i:s')) >= strtotime($solidaryMatching->getSolidaryUser()->getMMinTime()->format('H:i:s'))
                    ) {
                        $volunteer['schedule']['mWed'] = true;
                    }
                    if (
                        $solidaryMatching->getCriteria()->isWedCheck()
                        && strtotime($solidaryMatching->getCriteria()->getWedMinTime()->format('H:i:s')) < strtotime($solidaryMatching->getSolidaryUser()->getAMaxTime()->format('H:i:s'))
                        && strtotime($solidaryMatching->getCriteria()->getWedMaxTime()->format('H:i:s')) >= strtotime($solidaryMatching->getSolidaryUser()->getAMinTime()->format('H:i:s'))
                    ) {
                        $volunteer['schedule']['aWed'] = true;
                    }
                    if (
                        $solidaryMatching->getCriteria()->isWedCheck()
                        && strtotime($solidaryMatching->getCriteria()->getWedMinTime()->format('H:i:s')) < strtotime($solidaryMatching->getSolidaryUser()->getEMaxTime()->format('H:i:s'))
                        && strtotime($solidaryMatching->getCriteria()->getWedMaxTime()->format('H:i:s')) >= strtotime($solidaryMatching->getSolidaryUser()->getEMinTime()->format('H:i:s'))
                    ) {
                        $volunteer['schedule']['eWed'] = true;
                    }
                    if (
                        $solidaryMatching->getCriteria()->isThuCheck()
                        && strtotime($solidaryMatching->getCriteria()->getThuMinTime()->format('H:i:s')) < strtotime($solidaryMatching->getSolidaryUser()->getMMaxTime()->format('H:i:s'))
                        && strtotime($solidaryMatching->getCriteria()->getThuMaxTime()->format('H:i:s')) >= strtotime($solidaryMatching->getSolidaryUser()->getMMinTime()->format('H:i:s'))
                    ) {
                        $volunteer['schedule']['mThu'] = true;
                    }
                    if (
                        $solidaryMatching->getCriteria()->isThuCheck()
                        && strtotime($solidaryMatching->getCriteria()->getThuMinTime()->format('H:i:s')) < strtotime($solidaryMatching->getSolidaryUser()->getAMaxTime()->format('H:i:s'))
                        && strtotime($solidaryMatching->getCriteria()->getThuMaxTime()->format('H:i:s')) >= strtotime($solidaryMatching->getSolidaryUser()->getAMinTime()->format('H:i:s'))
                    ) {
                        $volunteer['schedule']['aThu'] = true;
                    }
                    if (
                        $solidaryMatching->getCriteria()->isThuCheck()
                        && strtotime($solidaryMatching->getCriteria()->getThuMinTime()->format('H:i:s')) < strtotime($solidaryMatching->getSolidaryUser()->getEMaxTime()->format('H:i:s'))
                        && strtotime($solidaryMatching->getCriteria()->getThuMaxTime()->format('H:i:s')) >= strtotime($solidaryMatching->getSolidaryUser()->getEMinTime()->format('H:i:s'))
                    ) {
                        $volunteer['schedule']['eThu'] = true;
                    }
                    if (
                        $solidaryMatching->getCriteria()->isFriCheck()
                        && strtotime($solidaryMatching->getCriteria()->getFriMinTime()->format('H:i:s')) < strtotime($solidaryMatching->getSolidaryUser()->getMMaxTime()->format('H:i:s'))
                        && strtotime($solidaryMatching->getCriteria()->getFriMaxTime()->format('H:i:s')) >= strtotime($solidaryMatching->getSolidaryUser()->getMMinTime()->format('H:i:s'))
                    ) {
                        $volunteer['schedule']['mFri'] = true;
                    }
                    if (
                        $solidaryMatching->getCriteria()->isFriCheck()
                        && strtotime($solidaryMatching->getCriteria()->getFriMinTime()->format('H:i:s')) < strtotime($solidaryMatching->getSolidaryUser()->getAMaxTime()->format('H:i:s'))
                        && strtotime($solidaryMatching->getCriteria()->getFriMaxTime()->format('H:i:s')) >= strtotime($solidaryMatching->getSolidaryUser()->getAMinTime()->format('H:i:s'))
                    ) {
                        $volunteer['schedule']['aFri'] = true;
                    }
                    if (
                        $solidaryMatching->getCriteria()->isFriCheck()
                        && strtotime($solidaryMatching->getCriteria()->getFriMinTime()->format('H:i:s')) < strtotime($solidaryMatching->getSolidaryUser()->getEMaxTime()->format('H:i:s'))
                        && strtotime($solidaryMatching->getCriteria()->getFriMaxTime()->format('H:i:s')) >= strtotime($solidaryMatching->getSolidaryUser()->getEMinTime()->format('H:i:s'))
                    ) {
                        $volunteer['schedule']['eFri'] = true;
                    }
                    if (
                        $solidaryMatching->getCriteria()->isSatCheck()
                        && strtotime($solidaryMatching->getCriteria()->getSatMinTime()->format('H:i:s')) < strtotime($solidaryMatching->getSolidaryUser()->getMMaxTime()->format('H:i:s'))
                        && strtotime($solidaryMatching->getCriteria()->getSatMaxTime()->format('H:i:s')) >= strtotime($solidaryMatching->getSolidaryUser()->getMMinTime()->format('H:i:s'))
                    ) {
                        $volunteer['schedule']['mSat'] = true;
                    }
                    if (
                        $solidaryMatching->getCriteria()->isSatCheck()
                        && strtotime($solidaryMatching->getCriteria()->getSatMinTime()->format('H:i:s')) < strtotime($solidaryMatching->getSolidaryUser()->getAMaxTime()->format('H:i:s'))
                        && strtotime($solidaryMatching->getCriteria()->getSatMaxTime()->format('H:i:s')) >= strtotime($solidaryMatching->getSolidaryUser()->getAMinTime()->format('H:i:s'))
                    ) {
                        $volunteer['schedule']['aSat'] = true;
                    }
                    if (
                        $solidaryMatching->getCriteria()->isSatCheck()
                        && strtotime($solidaryMatching->getCriteria()->getSatMinTime()->format('H:i:s')) < strtotime($solidaryMatching->getSolidaryUser()->getEMaxTime()->format('H:i:s'))
                        && strtotime($solidaryMatching->getCriteria()->getSatMaxTime()->format('H:i:s')) >= strtotime($solidaryMatching->getSolidaryUser()->getEMinTime()->format('H:i:s'))
                    ) {
                        $volunteer['schedule']['eSat'] = true;
                    }
                    if (
                        $solidaryMatching->getCriteria()->isSunCheck()
                        && strtotime($solidaryMatching->getCriteria()->getSunMinTime()->format('H:i:s')) < strtotime($solidaryMatching->getSolidaryUser()->getMMaxTime()->format('H:i:s'))
                        && strtotime($solidaryMatching->getCriteria()->getSunMaxTime()->format('H:i:s')) >= strtotime($solidaryMatching->getSolidaryUser()->getMMinTime()->format('H:i:s'))
                    ) {
                        $volunteer['schedule']['mSun'] = true;
                    }
                    if (
                        $solidaryMatching->getCriteria()->isSunCheck()
                        && strtotime($solidaryMatching->getCriteria()->getSunMinTime()->format('H:i:s')) < strtotime($solidaryMatching->getSolidaryUser()->getAMaxTime()->format('H:i:s'))
                        && strtotime($solidaryMatching->getCriteria()->getSunMaxTime()->format('H:i:s')) >= strtotime($solidaryMatching->getSolidaryUser()->getAMinTime()->format('H:i:s'))
                    ) {
                        $volunteer['schedule']['aSun'] = true;
                    }
                    if (
                        $solidaryMatching->getCriteria()->isSunCheck()
                        && strtotime($solidaryMatching->getCriteria()->getSunMinTime()->format('H:i:s')) < strtotime($solidaryMatching->getSolidaryUser()->getEMaxTime()->format('H:i:s'))
                        && strtotime($solidaryMatching->getCriteria()->getSunMaxTime()->format('H:i:s')) >= strtotime($solidaryMatching->getSolidaryUser()->getEMinTime()->format('H:i:s'))
                    ) {
                        $volunteer['schedule']['eSun'] = true;
                    }
                } else {
                    // punctual, we search the corresponding day
                    $key = '';

                    switch ($solidaryMatching->getCriteria()->getFromDate()->format('w')) {
                        case 0: $key = 'Sun';

                            break;

                        case 1: $key = 'Mon';

                            break;

                        case 2: $key = 'Tue';

                            break;

                        case 3: $key = 'Wed';

                            break;

                        case 4: $key = 'Thu';

                            break;

                        case 5: $key = 'Fri';

                            break;

                        case 6: $key = 'Sat';

                            break;
                    }
                    if (
                        strtotime($solidaryMatching->getCriteria()->getMinTime()->format('H:i:s')) < strtotime($solidaryMatching->getSolidaryUser()->getMMaxTime()->format('H:i:s'))
                        && strtotime($solidaryMatching->getCriteria()->getMaxTime()->format('H:i:s')) >= strtotime($solidaryMatching->getSolidaryUser()->getMMinTime()->format('H:i:s'))
                    ) {
                        $volunteer['schedule']['m'.$key] = true;
                    }
                    if (
                        strtotime($solidaryMatching->getCriteria()->getMinTime()->format('H:i:s')) < strtotime($solidaryMatching->getSolidaryUser()->getAMaxTime()->format('H:i:s'))
                        && strtotime($solidaryMatching->getCriteria()->getMaxTime()->format('H:i:s')) >= strtotime($solidaryMatching->getSolidaryUser()->getAMinTime()->format('H:i:s'))
                    ) {
                        $volunteer['schedule']['a'.$key] = true;
                    }
                    if (
                        strtotime($solidaryMatching->getCriteria()->getMinTime()->format('H:i:s')) < strtotime($solidaryMatching->getSolidaryUser()->getEMaxTime()->format('H:i:s'))
                        && strtotime($solidaryMatching->getCriteria()->getMaxTime()->format('H:i:s')) >= strtotime($solidaryMatching->getSolidaryUser()->getEMinTime()->format('H:i:s'))
                    ) {
                        $volunteer['schedule']['e'.$key] = true;
                    }
                }

                if (Proposal::TYPE_RETURN !== $solidaryMatching->getType()) {
                    $volunteers['outward'][] = $volunteer;
                } else {
                    $volunteers['return'][] = $volunteer;
                }
            }
        }

        $solidary->setAdmincarpools($carpools);
        $solidary->setAdmintransporters($volunteers);

        // set solutions and threads
        $solutions = [
            'drivers' => [],
            'threads' => [],
        ];
        foreach ($solidary->getSolidarySolutions() as $solution) {
            /**
             * @var SolidarySolution $solution
             */
            // set drivers
            if ($solution->getSolidaryMatching()->getSolidaryUser()) {
                // solution is a transporter
                $solutions['drivers'][] = [
                    'id' => $solution->getId(),
                    'matchingId' => $solution->getSolidaryMatching()->getId(),
                    'type' => SolidarySolution::TRANSPORTER,
                    'givenName' => $solution->getSolidaryMatching()->getSolidaryUser()->getUser()->getGivenName(),
                    'familyName' => $solution->getSolidaryMatching()->getSolidaryUser()->getUser()->getFamilyName(),
                    'telephone' => $solution->getSolidaryMatching()->getSolidaryUser()->getUser()->getTelephone(),
                    'avatar' => $solution->getSolidaryMatching()->getSolidaryUser()->getUser()->getAvatar(),
                    'userId' => $solution->getSolidaryMatching()->getSolidaryUser()->getUser()->getId(),
                    'status' => $solution->getSolidaryAsk() ? $solution->getSolidaryAsk()->getStatus() : SolidaryAsk::STATUS_ASKED,
                    'contacted' => $solution->getSolidaryAsk() ? true : false,
                    'centerPoint' => $solution->getSolidaryMatching()->getSolidaryUser()->getAddress()->jsonSerialize(),
                    'maxDistance' => $solution->getSolidaryMatching()->getSolidaryUser()->getMaxDistance(),
                    'mMinTime' => $solution->getSolidaryMatching()->getSolidaryUser()->getMMinTime(), 'mMaxTime' => $solution->getSolidaryMatching()->getSolidaryUser()->getMMaxTime(),
                    'aMinTime' => $solution->getSolidaryMatching()->getSolidaryUser()->getAMinTime(), 'aMaxTime' => $solution->getSolidaryMatching()->getSolidaryUser()->getAMaxTime(),
                    'eMinTime' => $solution->getSolidaryMatching()->getSolidaryUser()->getEMinTime(), 'eMaxTime' => $solution->getSolidaryMatching()->getSolidaryUser()->getEMaxTime(),
                    'mMon' => $solution->getSolidaryMatching()->getSolidaryUser()->hasMMon(), 'aMon' => $solution->getSolidaryMatching()->getSolidaryUser()->hasAMon(), 'eMon' => $solution->getSolidaryMatching()->getSolidaryUser()->hasEMon(),
                    'mTue' => $solution->getSolidaryMatching()->getSolidaryUser()->hasMTue(), 'aTue' => $solution->getSolidaryMatching()->getSolidaryUser()->hasATue(), 'eTue' => $solution->getSolidaryMatching()->getSolidaryUser()->hasETue(),
                    'mWed' => $solution->getSolidaryMatching()->getSolidaryUser()->hasMWed(), 'aWed' => $solution->getSolidaryMatching()->getSolidaryUser()->hasAWed(), 'eWed' => $solution->getSolidaryMatching()->getSolidaryUser()->hasEWed(),
                    'mThu' => $solution->getSolidaryMatching()->getSolidaryUser()->hasMThu(), 'aThu' => $solution->getSolidaryMatching()->getSolidaryUser()->hasAThu(), 'eThu' => $solution->getSolidaryMatching()->getSolidaryUser()->hasEThu(),
                    'mFri' => $solution->getSolidaryMatching()->getSolidaryUser()->hasMFri(), 'aFri' => $solution->getSolidaryMatching()->getSolidaryUser()->hasAFri(), 'eFri' => $solution->getSolidaryMatching()->getSolidaryUser()->hasEFri(),
                    'mSat' => $solution->getSolidaryMatching()->getSolidaryUser()->hasMSat(), 'aSat' => $solution->getSolidaryMatching()->getSolidaryUser()->hasASat(), 'eSat' => $solution->getSolidaryMatching()->getSolidaryUser()->hasESat(),
                    'mSun' => $solution->getSolidaryMatching()->getSolidaryUser()->hasMSun(), 'aSun' => $solution->getSolidaryMatching()->getSolidaryUser()->hasASun(), 'eSun' => $solution->getSolidaryMatching()->getSolidaryUser()->hasESun(),
                ];
            } elseif ($solution->getSolidaryMatching()->getMatching()) {
                // solution is a carpooler
                $asolution = [
                    'id' => $solution->getId(),
                    'matchingId' => $solution->getSolidaryMatching()->getId(),
                    'type' => SolidarySolution::CARPOOLER,
                    'givenName' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getUser()->getGivenName(),
                    'familyName' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getUser()->getFamilyName(),
                    'telephone' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getUser()->getTelephone(),
                    'avatar' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getUser()->getAvatar(),
                    'userId' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getUser()->getId(),
                    'way' => $solution->getSolidaryMatching()->getMatching()->getProposalRequest()->getType(),
                    'status' => $solution->getSolidaryAsk() ? $solution->getSolidaryAsk()->getStatus() : SolidaryAsk::STATUS_ASKED,
                    'contacted' => $solution->getSolidaryAsk() ? true : false,
                    'frequency' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getCriteria()->getFrequency(),
                    'carpoolerProposalType' => Proposal::TYPE_ONE_WAY == $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getType() ? 'oneway' : 'roundtrip',
                    'proposalType' => $solution->getSolidaryMatching()->getSolidaryMatchingLinked() ? 'roundtrip' : 'oneway',
                    'passenger' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getCriteria()->isPassenger(),
                    'driver' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getCriteria()->isDriver(),
                    'solidaryExclusive' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getCriteria()->isSolidaryExclusive(),
                ];
                foreach ($solution->getSolidaryMatching()->getMatching()->getProposalRequest()->getWaypoints() as $waypoint) {
                    /**
                     * @var Waypoint $waypoint
                     */
                    if (0 == $waypoint->getPosition()) {
                        $asolution['origin'] = $waypoint->getAddress()->jsonSerialize();
                    }
                    if ($waypoint->isDestination()) {
                        $asolution['destination'] = $waypoint->getAddress()->jsonSerialize();
                    }
                }
                $way = 'outward';
                if (Proposal::TYPE_RETURN == $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getType()) {
                    $way = 'return';
                }
                $asolution[$way] = [
                    'fromDate' => $solution->getSolidaryMatching()->getMatching()->getCriteria()->getFromDate(),
                    'fromTime' => $solution->getSolidaryMatching()->getMatching()->getCriteria()->getFromTime(),
                    'toDate' => $solution->getSolidaryMatching()->getMatching()->getCriteria()->getToDate(),
                    'carpoolerFromDate' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getCriteria()->getFromDate(),
                    'carpoolerFromTime' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getCriteria()->getFromTime(),
                    'carpoolerToDate' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getCriteria()->getToDate(),
                ];
                if (Criteria::FREQUENCY_REGULAR == $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getCriteria()->getFrequency()) {
                    $asolution['schedule'][$way] = [
                        'mon' => $solution->getSolidaryMatching()->getMatching()->getCriteria()->isMonCheck() ? $solution->getSolidaryMatching()->getMatching()->getCriteria()->getMonTime() : false,
                        'tue' => $solution->getSolidaryMatching()->getMatching()->getCriteria()->isTueCheck() ? $solution->getSolidaryMatching()->getMatching()->getCriteria()->getTueTime() : false,
                        'wed' => $solution->getSolidaryMatching()->getMatching()->getCriteria()->isWedCheck() ? $solution->getSolidaryMatching()->getMatching()->getCriteria()->getWedTime() : false,
                        'thu' => $solution->getSolidaryMatching()->getMatching()->getCriteria()->isThuCheck() ? $solution->getSolidaryMatching()->getMatching()->getCriteria()->getThuTime() : false,
                        'fri' => $solution->getSolidaryMatching()->getMatching()->getCriteria()->isFriCheck() ? $solution->getSolidaryMatching()->getMatching()->getCriteria()->getFriTime() : false,
                        'sat' => $solution->getSolidaryMatching()->getMatching()->getCriteria()->isSatCheck() ? $solution->getSolidaryMatching()->getMatching()->getCriteria()->getSatTime() : false,
                        'sun' => $solution->getSolidaryMatching()->getMatching()->getCriteria()->isSunCheck() ? $solution->getSolidaryMatching()->getMatching()->getCriteria()->getSunTime() : false,
                    ];
                    $asolution['carpoolerSchedule'][$way] = [
                        'mon' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getCriteria()->isMonCheck() ? $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getCriteria()->getMonTime() : false,
                        'tue' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getCriteria()->isTueCheck() ? $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getCriteria()->getTueTime() : false,
                        'wed' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getCriteria()->isWedCheck() ? $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getCriteria()->getWedTime() : false,
                        'thu' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getCriteria()->isThuCheck() ? $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getCriteria()->getThuTime() : false,
                        'fri' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getCriteria()->isFriCheck() ? $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getCriteria()->getFriTime() : false,
                        'sat' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getCriteria()->isSatCheck() ? $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getCriteria()->getSatTime() : false,
                        'sun' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getCriteria()->isSunCheck() ? $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getCriteria()->getSunTime() : false,
                    ];
                }
                foreach ($solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getWaypoints() as $waypoint) {
                    /**
                     * @var Waypoint $waypoint
                     */
                    if (0 == $waypoint->getPosition()) {
                        $asolution[$way]['origin'] = $waypoint->getAddress()->jsonSerialize();
                        if (Criteria::FREQUENCY_PUNCTUAL == $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getCriteria()->getFrequency()) {
                            /**
                             * @var DateTime $destinationTime
                             */
                            $destinationTime = clone $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getCriteria()->getFromTime();
                            $destinationTime->add(new \DateInterval('PT'.$solution->getSolidaryMatching()->getMatching()->getOriginalDuration().'S'));
                            $asolution[$way]['destinationTime'] = $destinationTime;
                        }
                    }
                    if ($waypoint->isDestination()) {
                        $asolution[$way]['destination'] = $waypoint->getAddress()->jsonSerialize();
                    }
                }
                // check for a matching return
                if ($solution->getSolidaryMatching()->getSolidaryMatchingLinked()) {
                    if ('outward' == $way) {
                        $way = 'return';
                    } else {
                        $way = 'outward';
                    }
                    $asolution[$way] = [
                        'fromDate' => $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getCriteria()->getFromDate(),
                        'fromTime' => $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getCriteria()->getFromTime(),
                        'toDate' => $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getCriteria()->getToDate(),
                        'carpoolerFromDate' => $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getProposalOffer()->getCriteria()->getFromDate(),
                        'carpoolerFromTime' => $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getProposalOffer()->getCriteria()->getFromTime(),
                        'carpoolerToDate' => $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getProposalOffer()->getCriteria()->getToDate(),
                    ];
                    if (Criteria::FREQUENCY_REGULAR == $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getProposalOffer()->getCriteria()->getFrequency()) {
                        $asolution['schedule'][$way] = [
                            'mon' => $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getCriteria()->isMonCheck() ? $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getCriteria()->getMonTime() : false,
                            'tue' => $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getCriteria()->isTueCheck() ? $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getCriteria()->getTueTime() : false,
                            'wed' => $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getCriteria()->isWedCheck() ? $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getCriteria()->getWedTime() : false,
                            'thu' => $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getCriteria()->isThuCheck() ? $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getCriteria()->getThuTime() : false,
                            'fri' => $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getCriteria()->isFriCheck() ? $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getCriteria()->getFriTime() : false,
                            'sat' => $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getCriteria()->isSatCheck() ? $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getCriteria()->getSatTime() : false,
                            'sun' => $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getCriteria()->isSunCheck() ? $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getCriteria()->getSunTime() : false,
                        ];
                        $asolution['carpoolerSchedule'][$way] = [
                            'mon' => $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getProposalOffer()->getCriteria()->isMonCheck() ? $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getProposalOffer()->getCriteria()->getMonTime() : false,
                            'tue' => $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getProposalOffer()->getCriteria()->isTueCheck() ? $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getProposalOffer()->getCriteria()->getTueTime() : false,
                            'wed' => $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getProposalOffer()->getCriteria()->isWedCheck() ? $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getProposalOffer()->getCriteria()->getWedTime() : false,
                            'thu' => $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getProposalOffer()->getCriteria()->isThuCheck() ? $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getProposalOffer()->getCriteria()->getThuTime() : false,
                            'fri' => $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getProposalOffer()->getCriteria()->isFriCheck() ? $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getProposalOffer()->getCriteria()->getFriTime() : false,
                            'sat' => $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getProposalOffer()->getCriteria()->isSatCheck() ? $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getProposalOffer()->getCriteria()->getSatTime() : false,
                            'sun' => $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getProposalOffer()->getCriteria()->isSunCheck() ? $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getProposalOffer()->getCriteria()->getSunTime() : false,
                        ];
                    }
                    foreach ($solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getProposalOffer()->getWaypoints() as $waypoint) {
                        /**
                         * @var Waypoint $waypoint
                         */
                        if (0 == $waypoint->getPosition()) {
                            $asolution[$way]['origin'] = $waypoint->getAddress()->jsonSerialize();
                            if (Criteria::FREQUENCY_PUNCTUAL == $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getProposalOffer()->getCriteria()->getFrequency()) {
                                /**
                                 * @var DateTime $destinationTime
                                 */
                                $destinationTime = clone $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getProposalOffer()->getCriteria()->getFromTime();
                                $destinationTime->add(new \DateInterval('PT'.$solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getMatching()->getOriginalDuration().'S'));
                                $asolution[$way]['destinationTime'] = $destinationTime;
                            }
                        }
                        if ($waypoint->isDestination()) {
                            $asolution[$way]['destination'] = $waypoint->getAddress()->jsonSerialize();
                        }
                    }
                } elseif ($solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getProposalLinked()) {
                    // no matching return, but the driver has a non matching return trip
                    if ('outward' == $way) {
                        $way = 'return';
                    } else {
                        $way = 'outward';
                    }
                    $asolution[$way] = [
                        'fromDate' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getProposalLinked()->getCriteria()->getFromDate(),
                        'fromTime' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getProposalLinked()->getCriteria()->getFromTime(),
                        'toDate' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getProposalLinked()->getCriteria()->getToDate(),
                    ];
                    if (Criteria::FREQUENCY_REGULAR == $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getProposalLinked()->getCriteria()->getFrequency()) {
                        $asolution['schedule'][$way] = [
                            'mon' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getProposalLinked()->getCriteria()->isMonCheck() ? $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getProposalLinked()->getCriteria()->getMonTime() : false,
                            'tue' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getProposalLinked()->getCriteria()->isTueCheck() ? $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getProposalLinked()->getCriteria()->getTueTime() : false,
                            'wed' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getProposalLinked()->getCriteria()->isWedCheck() ? $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getProposalLinked()->getCriteria()->getWedTime() : false,
                            'thu' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getProposalLinked()->getCriteria()->isThuCheck() ? $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getProposalLinked()->getCriteria()->getThuTime() : false,
                            'fri' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getProposalLinked()->getCriteria()->isFriCheck() ? $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getProposalLinked()->getCriteria()->getFriTime() : false,
                            'sat' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getProposalLinked()->getCriteria()->isSatCheck() ? $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getProposalLinked()->getCriteria()->getSatTime() : false,
                            'sun' => $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getProposalLinked()->getCriteria()->isSunCheck() ? $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getProposalLinked()->getCriteria()->getSunTime() : false,
                        ];
                    }
                    foreach ($solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getProposalLinked()->getWaypoints() as $waypoint) {
                        /**
                         * @var Waypoint $waypoint
                         */
                        if (0 == $waypoint->getPosition()) {
                            $asolution[$way]['origin'] = $waypoint->getAddress()->jsonSerialize();
                            if (Criteria::FREQUENCY_PUNCTUAL == $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getProposalLinked()->getCriteria()->getFrequency()) {
                                /**
                                 * @var DateTime $destinationTime
                                 */
                                $destinationTime = clone $solution->getSolidaryMatching()->getMatching()->getProposalOffer()->getProposalLinked()->getCriteria()->getFromTime();
                                // for the destination time, we have to use the outward time as there is no matching for the return...
                                $destinationTime->add(new \DateInterval('PT'.$solution->getSolidaryMatching()->getMatching()->getOriginalDuration().'S'));
                                $asolution[$way]['destinationTime'] = $destinationTime;
                            }
                        }
                        if ($waypoint->isDestination()) {
                            $asolution[$way]['destination'] = $waypoint->getAddress()->jsonSerialize();
                        }
                    }
                }
                $solutions['drivers'][] = $asolution;
            }
            // set threads
            $solutions['threads'][$solution->getId()] = $this->getThreadForSolution($solution);
        }
        // group outward and return (only one should have a thread => we use the same thread)
        foreach ($solidary->getSolidarySolutions() as $solution) {
            /**
             * @var SolidarySolution $solution
             */
            if ([] == $solutions['threads'][$solution->getId()] && $solution->getSolidarySolutionLinked() && isset($solutions['threads'][$solution->getSolidarySolutionLinked()->getId()]) && [] !== $solutions['threads'][$solution->getSolidarySolutionLinked()->getId()]) {
                $solutions['threads'][$solution->getId()] = $solutions['threads'][$solution->getSolidarySolutionLinked()->getId()];
            }
        }

        $solidary->setAdminsolutions($solutions);

        // set diary
        $diaries = [];
        foreach ($solidary->getDiaries() as $diary) {
            // @var Diary $diary
            $diaries[] = [
                'action' => $diary->getAction()->getName(),
                'comment' => $diary->getComment(),
                'progression' => $diary->getProgression(),
                'authorGivenName' => $diary->getAuthor()->getGivenName(),
                'authorFamilyName' => $diary->getAuthor()->getFamilyName(),
                'authorAvatar' => $diary->getAuthor()->getAvatar(),
                'userId' => $diary->getUser()->getId(),
                'givenName' => $diary->getUser()->getGivenName(),
                'familyName' => $diary->getUser()->getFamilyName(),
                'avatar' => $diary->getUser()->getAvatar(),
                'date' => $diary->getCreatedDate(),
            ];
        }
        $solidary->setAdmindiary($diaries);

        // get distance
        $solidary->setDistance($solidary->getProposal()->getCriteria()->getDirectionPassenger()->getDistance() / 1000);

        return $solidary;
    }

    /**
     * Get Solidary records.
     *
     * @param PaginatorInterface $solidaries The solidary objects
     *
     * @return null|array The solidary records
     */
    public function getSolidaries(PaginatorInterface $solidaries)
    {
        return $solidaries;
    }

    /**
     * Get manually available triggered actions.
     *
     * @return array
     */
    public function getActions()
    {
        return $this->actionRepository->getSolidaryActions();
    }

    /**
     * Add a solidary record.
     *
     * @param Solidary $solidary The solidary to add
     * @param array    $fields   The fields
     *
     * @return Solidary The solidary created
     */
    public function addSolidary(Solidary $solidary, array $fields)
    {
        // To create a new Solidary record, we need the following steps :
        // 1. create a SolidaryUser if the beneficiary is not already a solidary user
        // 2. create a SolidaryUserStructure, reflecting the status of the beneficiary within the structure (if the beneficiary has not already a SolidaryRecord in that structure)
        // 3. create SolidaryProofs if needed, linked to the SolidaryUserStructure
        // 4. prepare the SolidaryRecord with the Subject, which must be related to the proposal
        // 5. create a SolidaryRecord, linked to the SolidaryUserStructure, the Subject and Needs
        // 6. create a Proposal, linked with the SolidaryRecord, reflecting the journey needed for the beneficiary

        // Note : we create the solidary record before the proposal, therefor the proposal for the solidary record is null
        // it's because we need to link Matchings with SolidaryMatchnings, this is done using events during the matching process that we can't modify for consistency reasons !

        // first we perform some checkings !

        // check beneficiary
        if (!isset($fields['beneficiary'])) {
            throw new SolidaryException(SolidaryException::BENEFICIARY_REQUIRED);
        }

        // check structure
        if (!isset($fields['structure'])) {
            throw new SolidaryException(SolidaryException::STRUCTURE_REQUIRED);
        }
        if (!$structure = $this->structureRepository->find($fields['structure'])) {
            throw new SolidaryException(sprintf(SolidaryException::STRUCTURE_NOT_FOUND, $fields['structure']));
        }

        // check journey
        if (!isset($fields['origin'])) {
            throw new SolidaryException(SolidaryException::ORIGIN_REQUIRED);
        }
        if (!isset($fields['destination']) && (!isset($fields['destinationAny']) || !$fields['destinationAny'])) {
            throw new SolidaryException(SolidaryException::DESTINATION_REQUIRED);
        }

        // check frequency
        if (!isset($fields['regular'])) {
            throw new SolidaryException(SolidaryException::FREQUENCY_REQUIRED);
        }
        if ($fields['regular'] && !isset($fields['regularSchedules'])) {
            throw new SolidaryException(SolidaryException::REGULAR_SCHEDULES_REQUIRED);
        }
        if ($fields['regular'] && !isset($fields['regularDateChoice'])) {
            throw new SolidaryException(SolidaryException::REGULAR_DATE_CHOICE_REQUIRED);
        }
        if (!$fields['regular'] && !isset($fields['punctualOutwardDateChoice'])) {
            throw new SolidaryException(SolidaryException::PUNCTUAL_OUTWARD_DATE_CHOICE_REQUIRED);
        }
        if (!$fields['regular'] && !in_array($fields['punctualOutwardDateChoice'], Solidary::PUNCTUAL_OUTWARD_DATE_CHOICES)) {
            throw new SolidaryException(SolidaryException::PUNCTUAL_OUTWARD_DATE_CHOICE_INVALID);
        }
        if (!$fields['regular'] && !isset($fields['punctualOutwardTimeChoice'])) {
            throw new SolidaryException(SolidaryException::PUNCTUAL_OUTWARD_TIME_CHOICE_REQUIRED);
        }
        if (!$fields['regular'] && !in_array($fields['punctualOutwardTimeChoice'], Solidary::PUNCTUAL_TIME_CHOICES)) {
            throw new SolidaryException(SolidaryException::PUNCTUAL_OUTWARD_TIME_CHOICE_INVALID);
        }

        // 1 - create the SolidaryUser
        $beneficiary = null;
        $newUser = false;
        if (isset($fields['beneficiary']['id'])) {
            // check beneficiary is a valid user
            if (!$beneficiary = $this->userRepository->find($fields['beneficiary']['id'])) {
                throw new SolidaryException(sprintf(SolidaryException::BENEFICIARY_NOT_FOUND, $fields['beneficiary']['id']));
            }
            // check if beneficiary informations have been updated
            $beneficiary = $this->updateBeneficiary($beneficiary, $fields['beneficiary']);
        } else {
            // new user, check if an email was set
            if (!isset($fields['beneficiary']['email'])) {
                // no email set => we use the structure as base for subaddressing
                $fields['beneficiary']['email'] = $this->userManager->generateSubEmail($structure->getEmail());
            }
            $beneficiary = $this->userManager->createUserFromArray($fields['beneficiary']);
            $newUser = true;
        }
        // add the solidary role if not already granted
        $authItem = $this->authItemRepository->find($structure->hasBeneficiaryAutoApproval() ? AuthItem::ROLE_SOLIDARY_BENEFICIARY : AuthItem::ROLE_SOLIDARY_BENEFICIARY_CANDIDATE);
        if (!$this->userAuthAssignmentRepository->findByAuthItemAndUser($authItem, $beneficiary)) {
            $userAuthAssignment = new UserAuthAssignment();
            $userAuthAssignment->setAuthItem($authItem);
            $beneficiary->addUserAuthAssignment($userAuthAssignment);
        }
        $this->entityManager->persist($beneficiary);

        // check if the beneficiary is already a SolidaryUser
        $solidaryUser = null;
        if (
            (isset($fields['beneficiary']['id']) && !$solidaryUser = $this->solidaryUserRepository->findByUserId($fields['beneficiary']['id']))
            || $newUser
        ) {
            // not already a solidary user, we need to create a new one
            $solidaryUser = new SolidaryUser();
            $solidaryUser->setBeneficiary(true);
            $solidaryUser->setUser($beneficiary);
            $solidaryUser->setAddress($beneficiary->getHomeAddress());
            $solidaryUser->setMaxDistance(SolidaryUser::DEFAULT_MAX_DISTANCE);
        } else {
            // already a solidary user, we ensure that it is beneficiary
            $solidaryUser->setBeneficiary(true);
        }
        $this->entityManager->persist($solidaryUser);

        // 2 - create the SolidaryUserStructure
        $solidaryUserStructure = null;
        $createSolidaryUserStructure = false;
        if ($solidaryUser->getId() && !$solidaryUserStructure = $this->solidaryUserStructureRepository->findByStructureAndSolidaryUser($structure->getId(), $solidaryUser->getId())) {
            $createSolidaryUserStructure = true;
        } elseif (!$solidaryUser->getId()) {
            $createSolidaryUserStructure = true;
        }

        if ($createSolidaryUserStructure) {
            // no SolidaryUserStructureFound, we need to create a new one
            $solidaryUserStructure = new SolidaryUserStructure();
            $solidaryUserStructure->setSolidaryUser($solidaryUser);
            $solidaryUserStructure->setStructure($structure);
            $solidaryUserStructure->setStatus($structure->hasBeneficiaryAutoApproval() ? SolidaryUserStructure::STATUS_ACCEPTED : SolidaryUserStructure::STATUS_PENDING);
            $this->entityManager->persist($solidaryUserStructure);
        }

        $solidary->setSolidaryUserStructure($solidaryUserStructure);

        // 3 - create the proofs
        // we only set the basic proofs, not the files that would be sent on a separate call
        if (isset($fields['proofs'])) {
            foreach ($fields['proofs'] as $aproof) {
                if (!isset($aproof['id'])) {
                    throw new SolidaryException(SolidaryException::STRUCTURE_PROOF_ID_REQUIRED);
                }
                // if (!isset($aproof['value'])) {
                //     throw new SolidaryException(sprintf(SolidaryException::STRUCTURE_PROOF_VALUE_REQUIRED, $aproof['id']));
                // }
                if (!$structureProof = $this->structureProofRepository->find($aproof['id'])) {
                    throw new SolidaryException(sprintf(SolidaryException::STRUCTURE_PROOF_NOT_FOUND, $aproof['id']));
                }
                if (is_null($aproof['value']) && $structureProof->isMandatory() && !$structureProof->isFile()) {
                    throw new SolidaryException(SolidaryException::STRUCTURE_PROOF_VALUE_REQUIRED);
                }
                // we skip null values (meaning the proof is not mandatory and has been omitted during the edition)
                if (!$structureProof->isFile() && !is_null($aproof['value'])) {
                    $proof = new Proof();
                    $proof->setStructureProof($structureProof);
                    $proof->setValue($aproof['value']);
                    $solidaryUserStructure->addProof($proof);
                    $this->entityManager->persist($proof);
                }
            }
        }

        // 4 - prepare the subject
        if (isset($fields['adminsubject'])) {
            if ($subject = $this->subjectRepository->find($fields['adminsubject'])) {
                $solidary->setSubject($subject);
            } else {
                throw new SolidaryException(sprintf(SolidaryException::SUBJECT_NOT_FOUND, $fields['adminsubject']));
            }
        }
        if (isset($fields['additionalSubject'])) {
            $subject = new Subject();
            $subject->setLabel($fields['additionalSubject']);
            $subject->setStructure($structure);
            $subject->setPrivate(true);
            $this->entityManager->persist($subject);
            $solidary->setSubject($subject);
        }

        // 5 - create the SolidaryRecord

        // set original frequency
        $solidary->setFrequency($fields['regular'] ? Criteria::FREQUENCY_REGULAR : Criteria::FREQUENCY_PUNCTUAL);

        // set status
        $solidary->setStatus(Solidary::STATUS_CREATED);

        // set progression
        $solidary->setProgression(0);

        // create needs
        if (isset($fields['needs'])) {
            foreach ($fields['needs'] as $need) {
                if ($need = $this->needRepository->find($need)) {
                    $solidary->addNeed($need);
                } else {
                    throw new SolidaryException(sprintf(SolidaryException::NEED_NOT_FOUND, $need));
                }
            }
        }
        if (isset($fields['additionalNeed'])) {
            $need = new Need();
            $need->setLabel($fields['additionalNeed']);
            $need->setPrivate(true);
            // set the solidary as origin
            $need->setSolidary($solidary);
            $this->entityManager->persist($need);
            $solidary->addNeed($need);
        }

        // persist the solidary record
        $this->entityManager->persist($solidary);

        // we need to flush here as we are now about to post the ad => the users need to be persisted
        $this->entityManager->flush();

        // 6 - create the proposal
        $params = [
            'origin' => $fields['origin'],
            'destination' => isset($fields['destinationAny']) && $fields['destinationAny'] ? null : $fields['destination'],
            'regular' => $fields['regular'],
            'poster' => $this->poster->getId(),
            'beneficiary' => $beneficiary->getId(),
            'subject' => $subject->getId(),
            'solidary' => $solidary,
        ];
        if (isset($fields['punctualOutwardMinDate'])) {
            $params['punctualOutwardMinDate'] = $fields['punctualOutwardMinDate'];
        }
        if (isset($fields['punctualOutwardMaxDate'])) {
            $params['punctualOutwardMaxDate'] = $fields['punctualOutwardMaxDate'];
        }
        if (isset($fields['punctualOutwardMinTime'])) {
            $params['punctualOutwardMinTime'] = $fields['punctualOutwardMinTime'];
        }
        if (isset($fields['punctualOutwardDateChoice'])) {
            $params['punctualOutwardDateChoice'] = $fields['punctualOutwardDateChoice'];
            $solidary->setPunctualOutwardDateChoice($fields['punctualOutwardDateChoice']);
        }
        if (isset($fields['punctualOutwardTimeChoice'])) {
            $params['punctualOutwardTimeChoice'] = $fields['punctualOutwardTimeChoice'];
            $solidary->setPunctualOutwardTimeChoice($fields['punctualOutwardTimeChoice']);
        }
        if (isset($fields['punctualReturnDateChoice'])) {
            $params['punctualReturnDateChoice'] = $fields['punctualReturnDateChoice'];
            $solidary->setPunctualReturnDateChoice($fields['punctualReturnDateChoice']);
        }
        if (isset($fields['punctualReturnDate'])) {
            $params['punctualReturnDate'] = $fields['punctualReturnDate'];
        }
        if (isset($fields['punctualReturnTime'])) {
            $params['punctualReturnTime'] = $fields['punctualReturnTime'];
        }
        if (isset($fields['regularDateChoice'])) {
            $solidary->setRegularDateChoice($fields['regularDateChoice']);
        }
        if (isset($fields['regularMinDate'])) {
            $params['regularMinDate'] = $fields['regularMinDate'];
        }
        if (isset($fields['regularMaxDate'])) {
            $params['regularMaxDate'] = $fields['regularMaxDate'];
        }
        if (isset($fields['regularSchedules'])) {
            $params['regularSchedules'] = $fields['regularSchedules'];
        }
        $ad = $this->createAdFromArray($params, $this->getTimeAndMarginForStructure($structure));
        $solidary->setProposal($this->proposalRepository->find($ad->getId()));

        // 7. solidary transport matching
        $this->solidaryTransportMatcher->match($solidary);

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

        // link potential outward and return solidaryMatchings that would have not been made yet (as the link between Matchings are made after the SolidaryMatchings)
        $this->solidaryMatchingRepository->linkRelatedSolidaryMatchings($solidary->getId());

        // send an event to warn that a SolidaryRecord has been created
        $event = new SolidaryCreatedEvent($this->poster, $solidary);
        $this->eventDispatcher->dispatch(SolidaryCreatedEvent::NAME, $event);

        // read the solidary record again to get the last data (events should have updated it !)
        return $this->solidaryRepository->find($solidary->getId());
    }

    /**
     * Create a proof related to a solidary record.
     *
     * @param $file                         The file representing the proof
     * @param string $filename         The filename of the file
     * @param int    $solidaryId       The solidary record id
     * @param int    $structureProofId The structure proof id
     *
     * @return Proof
     */
    public function createProof(?File $file = null, ?string $filename = null, ?int $solidaryId = null, ?int $structureProofId = null)
    {
        if (!$solidaryId) {
            throw new SolidaryException(SolidaryException::SOLIDARY_ID_REQUIRED);
        }
        if (!$solidary = $this->solidaryRepository->find($solidaryId)) {
            throw new SolidaryException(sprintf(SolidaryException::SOLIDARY_NOT_FOUND, $solidaryId));
        }
        if (!$structureProofId) {
            throw new SolidaryException(SolidaryException::STRUCTURE_PROOF_ID_REQUIRED);
        }
        if (!$structureProof = $this->structureProofRepository->find($structureProofId)) {
            throw new SolidaryException(sprintf(SolidaryException::STRUCTURE_PROOF_NOT_FOUND, $structureProofId));
        }
        if ($structureProof->isFile() && !$file) {
            throw new SolidaryException(sprintf(SolidaryException::STRUCTURE_PROOF_FILE_REQUIRED, $structureProofId));
        }

        // check if the proof is an update of an existing proof
        foreach ($solidary->getSolidaryUserStructure()->getProofs() as $proof) {
            /**
             * @var Proof $proof
             */
            if ($proof->getStructureProof()->getId() == $structureProofId) {
                // update an existing proof => remove the old proof
                $solidary->getSolidaryUserStructure()->removeProof($proof);

                break;
            }
        }

        $proof = new Proof();
        $proof->setFile($file);

        if (!is_null($filename)) {
            $fileName = $this->fileManager->sanitize($filename);
        } else {
            $fileName = $this->fileManager->sanitize(microtime());
        }
        $proof->setFileName($solidary->getId().'-'.$fileName);

        $proof->setStructureProof($structureProof);
        $proof->setSolidaryUserStructure($solidary->getSolidaryUserStructure());

        return $proof;
    }

    /**
     * Create a new SolidaryMatching from a carpool Matching.
     *
     * @param Matching $matching The matching
     * @param Solidary $solidary The solidary record
     */
    public function createSolidaryMatchingFromCarpoolMatching(Matching $matching, Solidary $solidary)
    {
        $solidaryMatching = new SolidaryMatching();
        $solidaryMatching->setMatching($matching);
        $solidaryMatching->setSolidary($solidary);
        $solidaryMatching->setType($matching->getProposalRequest()->getType());
        $solidary->addSolidaryMatching($solidaryMatching);
        $this->entityManager->persist($solidaryMatching);
        $this->entityManager->persist($solidary);
        $this->entityManager->flush();
        $this->logger->info('SolidaryManager : persist the SolidaryMatching for matching '.$matching->getId().' | '.(new \DateTime('UTC'))->format('Ymd H:i:s.u'));
    }

    /**
     * Patch a solidary record.
     *
     * @param Solidary $solidary The solidary to update
     * @param array    $fields   The updated fields
     *
     * @return Solidary The solidary updated
     */
    public function patchSolidary(Solidary $solidary, array $fields)
    {
        // 1 - check for non destructive updates (no need to create a new solidary record)

        // check if a new animation has been made
        if (array_key_exists('animation', $fields)) {
            return $this->addAnimation($solidary, $fields['animation']);
        }

        // check if a new driver has been selected
        if (array_key_exists('solution', $fields)) {
            return $this->addSolution($solidary, $fields['solution']);
        }

        // check if a new message has been sent
        if (array_key_exists('message', $fields)) {
            return $this->addMessage($solidary, $fields['message']);
        }

        $updated = false;

        // check if new proofs were posted
        if (array_key_exists('proofs', $fields)) {
            $updated = true;
            $this->updateProofs($solidary, $fields['proofs']);
        }

        // check if new needs were posted
        if (array_key_exists('needs', $fields) || array_key_exists('additionalNeed', $fields)) {
            $updated = true;
            $this->updateNeeds($solidary, array_key_exists('needs', $fields) ? $fields['needs'] : null, array_key_exists('additionalNeed', $fields) ? $fields['additionalNeed'] : false);
        }

        // check if subject has changed
        if (array_key_exists('adminsubject', $fields)) {
            $updated = true;
            if ($subject = $this->subjectRepository->find($fields['adminsubject'])) {
                // check if current subject is private => if so, remove it !
                if ($solidary->getSubject()->isPrivate()) {
                    $this->entityManager->remove($solidary->getSubject());
                }
                $solidary->setSubject($subject);
                $solidary->getProposal()->setSubject($subject);
            } else {
                throw new SolidaryException(sprintf(SolidaryException::SUBJECT_NOT_FOUND, $fields['adminsubject']));
            }
        } elseif (isset($fields['additionalSubject'])) {
            $updated = true;
            if (!is_null($solidary->getSubject()) && $solidary->getSubject()->isPrivate()) {
                // update an existing additional subject
                $solidary->getSubject()->setLabel($fields['additionalSubject']);
            } else {
                $subject = new Subject();
                $subject->setLabel($fields['additionalSubject']);
                $subject->setStructure($solidary->getSolidaryUserStructure()->getStructure());
                $subject->setPrivate(true);
                $this->entityManager->persist($subject);
                $solidary->setSubject($subject);
                $solidary->getProposal()->setSubject($subject);
            }
        }

        // check if beneficiary was updated
        if (isset($fields['beneficiary'])) {
            $solidary->getSolidaryUserStructure()->getSolidaryUser()->setUser($this->updateBeneficiary($solidary->getSolidaryUserStructure()->getSolidaryUser()->getUser(), $fields['beneficiary']));
            $this->entityManager->persist($solidary);
            $updated = true;
        }

        if ($updated) {
            $this->entityManager->flush();
        }

        // 2 - check for potentially destructive updates (need to create a new solidary record)

        // we need a complete solidary to check
        $solidary = $this->getSolidary($solidary->getId());

        // check if (origin / destination has changed)
        foreach ($solidary->getProposal()->getWaypoints() as $waypoint) {
            /**
             * @var Waypoint $waypoint
             */
            if (0 == $waypoint->getPosition()) {
                if (isset($fields['origin'])) {
                    if (!$waypoint->getAddress()->isSame($fields['origin'])) {
                        return $this->duplicateSolidary($solidary, $fields);
                    }
                }
            } elseif ($waypoint->isDestination()) {
                if (isset($fields['destination'])) {
                    if (!$waypoint->getAddress()->isSame($fields['destination'])) {
                        return $this->duplicateSolidary($solidary, $fields);
                    }
                }
            }
        }
        if (isset($fields['destinationAny']) && $fields['destinationAny']) {
            return $this->duplicateSolidary($solidary, $fields);
        }

        // check if dates/times have changed
        if (isset($fields['regular'])) {
            // frequency has changed, we perform some checkings
            if ($fields['regular'] && !isset($fields['regularSchedules'])) {
                throw new SolidaryException(SolidaryException::REGULAR_SCHEDULES_REQUIRED);
            }
            if ($fields['regular'] && !isset($fields['regularDateChoice'])) {
                throw new SolidaryException(SolidaryException::REGULAR_DATE_CHOICE_REQUIRED);
            }
            if (!$fields['regular'] && !isset($fields['punctualOutwardDateChoice'])) {
                throw new SolidaryException(SolidaryException::PUNCTUAL_OUTWARD_DATE_CHOICE_REQUIRED);
            }
            if (!$fields['regular'] && !in_array($fields['punctualOutwardDateChoice'], Solidary::PUNCTUAL_OUTWARD_DATE_CHOICES)) {
                throw new SolidaryException(SolidaryException::PUNCTUAL_OUTWARD_DATE_CHOICE_INVALID);
            }
            if (!$fields['regular'] && !isset($fields['punctualOutwardTimeChoice'])) {
                throw new SolidaryException(SolidaryException::PUNCTUAL_OUTWARD_TIME_CHOICE_REQUIRED);
            }
            if (!$fields['regular'] && !in_array($fields['punctualOutwardTimeChoice'], Solidary::PUNCTUAL_TIME_CHOICES)) {
                throw new SolidaryException(SolidaryException::PUNCTUAL_OUTWARD_TIME_CHOICE_INVALID);
            }

            return $this->duplicateSolidary($solidary, $fields);
        }

        if (!isset($fields['punctualOutwardTimeChoice'])) {
            $fields['punctualOutwardTimeChoice'] = $solidary->getPunctualOutwardTimeChoice();
        }

        if (!isset($fields['punctualOutwardDateChoice'])) {
            $fields['punctualOutwardDateChoice'] = $solidary->getPunctualOutwardDateChoice();
        }

        if (!isset($fields['punctualReturnDateChoice'])) {
            $fields['punctualReturnDateChoice'] = $solidary->getPunctualReturnDateChoice();
        }

        if (
            (isset($fields['regularSchedules']) && Criteria::FREQUENCY_REGULAR == $solidary->getProposal()->getCriteria()->getFrequency())
            || (isset($fields['punctualOutwardDateChoice']))
            || (isset($fields['punctualOutwardTimeChoice']))
            || (isset($fields['punctualReturnDateChoice']))
            || (isset($fields['punctualOutwardMinDate']))
            || (isset($fields['punctualOutwardMinTime']))
            || (isset($fields['punctualReturnDate']))
            || (isset($fields['punctualReturnTime']))
            || (isset($fields['regularMinDate']))
            || (isset($fields['regularMaxDate']))
        ) {
            return $this->duplicateSolidary($solidary, $fields);
        }

        return $solidary;
    }

    /**
     * Delete a solidary record.
     *
     * @param Solidary $solidary The solidary to delete
     */
    public function deleteSolidary(Solidary $solidary)
    {
        $this->entityManager->remove($solidary);
        $this->entityManager->flush();
    }

    /**
     * Internal method used to get the regular schedules for a given proposal and a given day.
     *
     * @param Proposal $proposal  The proposal
     * @param int      $num       The number of the day in the week
     * @param string   $day       The shorten name of the day (3 letters)
     * @param array    $schedules The resulting schedules (passed by reference)
     */
    private function treatDay(Proposal $proposal, int $num, string $day, array &$schedules)
    {
        $checkMethod = 'is'.$day.'Check';
        $timeMethod = 'get'.$day.'Time';
        $outwardChecked = $proposal->getCriteria()->{$checkMethod}() && $proposal->getCriteria()->{$timeMethod}();
        $returnChecked =
            Proposal::TYPE_OUTWARD == $proposal->getType()
            && $proposal->getProposalLinked()
            && $proposal->getProposalLinked()->getCriteria()->{$checkMethod}()
            && $proposal->getProposalLinked()->getCriteria()->{$timeMethod}();

        if ($outwardChecked || $returnChecked) {
            $foundSchedule = false;
            foreach ($schedules as $key => $schedule) {
                if ($outwardChecked && $returnChecked) {
                    if (
                        $schedule['outwardTime'] == $proposal->getCriteria()->{$timeMethod}()
                        && $schedule['returnTime'] == $proposal->getProposalLinked()->getCriteria()->{$timeMethod}()
                    ) {
                        $schedules[$key][strtolower($day)] = true;
                        $foundSchedule = true;

                        break;
                    }
                } elseif ($outwardChecked) {
                    if ($schedule['outwardTime'] == $proposal->getCriteria()->{$timeMethod}() && null == $schedule['returnTime']) {
                        $schedules[$key][strtolower($day)] = true;
                        $foundSchedule = true;

                        break;
                    }
                } elseif ($returnChecked) {
                    if ($schedule['returnTime'] == $proposal->getProposalLinked()->getCriteria()->{$timeMethod}() && null == $schedule['outwardTime']) {
                        $schedules[$key][strtolower($day)] = true;
                        $foundSchedule = true;

                        break;
                    }
                }
            }
            if (!$foundSchedule) {
                $schedules[] = $this->createSchedule($num, $proposal->getCriteria()->{$timeMethod}(), $returnChecked ? $proposal->getProposalLinked()->getCriteria()->{$timeMethod}() : null);
            }
        }
    }

    /**
     * Internal method used to build a schedule array.
     *
     * @param int            $num         The number of the day in the week
     * @param null|\DateTime $outwardTime The outward time
     * @param null|\DateTime $returnTime  The return time
     *
     * @return array The schedule
     */
    private function createSchedule(int $num, ?DateTime $outwardTime = null, ?DateTime $returnTime = null)
    {
        return [
            'mon' => 0 == $num,
            'tue' => 1 == $num,
            'wed' => 2 == $num,
            'thu' => 3 == $num,
            'fri' => 4 == $num,
            'sat' => 5 == $num,
            'sun' => 6 == $num,
            'outwardTime' => $outwardTime,
            'returnTime' => $returnTime,
        ];
    }

    /**
     * Get a full message thread for a given SolidarySolution.
     *
     * @param SolidarySolution $solution The solidary solution
     *
     * @return array The thread, as a list of messages ordered by date desc
     */
    private function getThreadForSolution(SolidarySolution $solution)
    {
        $thread = [];
        // if the solution has no associated ask, there are no messages !
        if (!$solution->getSolidaryAsk()) {
            return $thread;
        }

        // we will loop through the ask histories to find the first ask history with an associated message
        // then, from this first message, we will be able to get the whole thread
        foreach ($solution->getSolidaryAsk()->getSolidaryAskHistories() as $solidaryAskHistory) {
            /**
             * @var SolidaryAskHistory $solidaryAskHistory
             */
            if ($solidaryAskHistory->getMessage()) {
                $completeThread = $this->internalMessageManager->getCompleteThread($solidaryAskHistory->getMessage()->getId());
                // we complete the messages with all the necessary informations
                foreach ($completeThread as $message) {
                    // @var Message $message
                    $thread[] = [
                        'posterId' => $message->getUser()->getId(),
                        'posterGivenName' => $message->getUser()->getGivenName(),
                        'posterFamilyName' => $message->getUser()->getFamilyName(),
                        'posterAvatar' => $message->getUser()->getAvatar(),
                        'delegateGivenName' => $message->getUserDelegate() ? $message->getUserDelegate()->getGivenName() : null,
                        'delegateFamilyName' => $message->getUserDelegate() ? $message->getUserDelegate()->getFamilyName() : null,
                        'delegateAvatar' => $message->getUserDelegate() ? $message->getUserDelegate()->getAvatar() : null,
                        'recipientGivenName' => $message->getRecipients()[0]->getUser()->getGivenName(),
                        'recipientFamilyName' => $message->getRecipients()[0]->getUser()->getFamilyName(),
                        'recipientAvatar' => $message->getRecipients()[0]->getUser()->getAvatar(),
                        'text' => $message->getText(),
                        'date' => $message->getCreatedDate(),
                    ];
                }

                break;
            }
        }

        return $thread;
    }

    /**
     * Duplicate a solidary record, when the original solidary record needs to be deeply updated.
     *
     * @param Solidary $solidary The solidary to duplicate
     * @param array    $fields   The fields to update the solidary
     *
     * @return Solidary The new solidary
     */
    private function duplicateSolidary(Solidary $solidary, array $fields): Solidary
    {
        $newSolidary = clone $solidary;
        $newSolidary->setSolidary($solidary);
        $newSolidary->setProgression(0);
        $newSolidary->setStatus(Solidary::STATUS_CREATED);

        $regular = Criteria::FREQUENCY_REGULAR == $solidary->getProposal()->getCriteria()->getFrequency();
        if (isset($fields['regular'])) {
            if ($fields['regular'] && Criteria::FREQUENCY_REGULAR != $solidary->getProposal()->getCriteria()->getFrequency()) {
                $regular = true;
            } elseif (!$fields['regular'] && Criteria::FREQUENCY_REGULAR == $solidary->getProposal()->getCriteria()->getFrequency()) {
                $regular = false;
            }
        }

        $newSolidary->setFrequency($regular ? Criteria::FREQUENCY_REGULAR : Criteria::FREQUENCY_PUNCTUAL);

        // check subject
        if ($solidary->getSubject()->isPrivate()) {
            $newSolidary->setSubject(clone $solidary->getSubject());
        }

        // check needs
        foreach ($solidary->getNeeds() as $need) {
            /**
             *  @var Need $need
             */
            if ($need->isPrivate()) {
                $newNeed = clone $need;
                $newNeed->setSolidary($newSolidary);
                $newSolidary->addNeed($newNeed);
            } else {
                $newSolidary->addNeed($need);
            }
        }

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

        // create the new proposal
        // origin and destination
        $origin = isset($fields['origin']) ? $fields['origin'] : null;
        $destination = isset($fields['destination']) ? $fields['destination'] : null;
        $destinationAny = isset($fields['destinationAny']) && $fields['destinationAny'] ? true : false;
        if (!$origin || !$destination) {
            foreach ($solidary->getProposal()->getWaypoints() as $waypoint) {
                /**
                 * @var Waypoint $waypoint
                 */
                if (0 == $waypoint->getPosition() && !$origin) {
                    $origin = $waypoint->getAddress()->jsonSerialize();
                } elseif ($waypoint->isDestination() && !$destination) {
                    $destination = $waypoint->getAddress()->jsonSerialize();
                }
            }
        }
        if ($destinationAny) {
            $destination = null;
        }
        $params = [
            'origin' => $origin,
            'destination' => $destination,
            'regular' => $regular,
            'poster' => $this->poster->getId(),
            'beneficiary' => $solidary->getSolidaryUserStructure()->getSolidaryUser()->getUser()->getId(),
            'subject' => $newSolidary->getSubject()->getId(),
            'solidary' => $newSolidary,
        ];
        // create the dates and times (mix from potential new dates and times and original dates and times)
        if (!$regular) {
            if (isset($fields['punctualOutwardMinDate'])) {
                $params['punctualOutwardMinDate'] = $fields['punctualOutwardMinDate'];
            } else {
                $params['punctualOutwardMinDate'] = $solidary->getProposal()->getCriteria()->getFromDate()->format('Y-m-d');
            }
            if (isset($fields['punctualOutwardMaxDate'])) {
                $params['punctualOutwardMaxDate'] = $fields['punctualOutwardMaxDate'];
            } elseif ($solidary->getProposal()->getCriteria()->getToDate()) {
                $params['punctualOutwardMaxDate'] = $solidary->getProposal()->getCriteria()->getToDate()->format('Y-m-d');
            }
            if (isset($fields['punctualOutwardMinTime'])) {
                $params['punctualOutwardMinTime'] = $fields['punctualOutwardMinTime'];
            } else {
                // we use the criteria time if it's set, otherwise we use the monTime (as in this case it should be a flexible proposal, all days are checked)
                if ($solidary->getProposal()->getCriteria()->getFromTime()) {
                    $params['punctualOutwardMinTime'] = $solidary->getProposal()->getCriteria()->getFromTime()->format('H:i');
                } else {
                    $params['punctualOutwardMinTime'] = $solidary->getProposal()->getCriteria()->getMonTime()->format('H:i');
                }
            }
            if (isset($fields['punctualOutwardDateChoice'])) {
                $params['punctualOutwardDateChoice'] = $fields['punctualOutwardDateChoice'];
                $newSolidary->setPunctualOutwardDateChoice($fields['punctualOutwardDateChoice']);
            } else {
                $params['punctualOutwardDateChoice'] = $newSolidary->getPunctualOutwardDateChoice();
                $params['punctualOutwardMinDate'] = $solidary->getProposal()->getCriteria()->getFromDate()->format('Y-m-d');

                switch ($params['punctualOutwardDateChoice']) {
                    case Solidary::PUNCTUAL_OUTWARD_DATE_CHOICE_DATE:
                        break;

                    default:
                        $params['punctualOutwardMaxDate'] = $solidary->getProposal()->getCriteria()->getToDate()->format('Y-m-d');

                        break;
                }
            }
            if (isset($fields['punctualOutwardTimeChoice'])) {
                $params['punctualOutwardTimeChoice'] = $fields['punctualOutwardTimeChoice'];
                $newSolidary->setPunctualOutwardTimeChoice($fields['punctualOutwardTimeChoice']);
            } else {
                $params['punctualOutwardTimeChoice'] = $newSolidary->getPunctualOutwardTimeChoice();
                $params['punctualOutwardMinTime'] = $solidary->getProposal()->getCriteria()->getFromTime()->format('H:i');
            }
            if (isset($fields['punctualReturnDate'])) {
                $params['punctualReturnDate'] = $fields['punctualReturnDate'];
            } elseif ($solidary->getProposal()->getProposalLinked()) {
                $params['punctualReturnDate'] = $solidary->getProposal()->getProposalLinked()->getCriteria()->getFromDate()->format('Y-m-d');
            }
            if (isset($fields['punctualReturnTime'])) {
                $params['punctualReturnTime'] = $fields['punctualReturnTime'];
            } elseif ($solidary->getProposal()->getProposalLinked()) {
                // we use the criteria time if it's set, otherwise we use the monTime (as in this case it should be a flexible proposal, all days are checked)
                if ($solidary->getProposal()->getProposalLinked()->getCriteria()->getFromTime()) {
                    $params['punctualReturnTime'] = $solidary->getProposal()->getProposalLinked()->getCriteria()->getFromTime()->format('H:i');
                } else {
                    $params['punctualReturnTime'] = $solidary->getProposal()->getProposalLinked()->getCriteria()->getMonTime()->format('H:i');
                }
            }
            if (isset($fields['punctualReturnDateChoice'])) {
                $params['punctualReturnDateChoice'] = $fields['punctualReturnDateChoice'];
                $newSolidary->setPunctualReturnDateChoice($fields['punctualReturnDateChoice']);
            } else {
                $params['punctualReturnDateChoice'] = $newSolidary->getPunctualReturnDateChoice();

                switch ($params['punctualReturnDateChoice']) {
                    case Solidary::PUNCTUAL_RETURN_DATE_CHOICE_NULL:
                        break;

                    default:
                        $params['punctualReturnDate'] = $solidary->getProposal()->getProposalLinked()->getCriteria()->getFromDate()->format('Y-m-d');
                        $params['punctualReturnTime'] = $solidary->getProposal()->getProposalLinked()->getCriteria()->getFromTime()->format('H:i');

                        break;
                }
            }
        } else {
            if (isset($fields['regularDateChoice'])) {
                $params['regularDateChoice'] = $fields['regularDateChoice'];
                $newSolidary->setRegularDateChoice($fields['regularDateChoice']);
            } else {
                $params['regularDateChoice'] = $newSolidary->getRegularDateChoice();
            }
            if (isset($fields['regularMinDate'])) {
                $params['regularMinDate'] = $fields['regularMinDate'];
            } else {
                $params['regularMinDate'] = $solidary->getProposal()->getCriteria()->getFromDate()->format('Y-m-d');
            }
            if (isset($fields['regularMaxDate'])) {
                $params['regularMaxDate'] = $fields['regularMaxDate'];
            } else {
                $params['regularMaxDate'] = $solidary->getProposal()->getCriteria()->getToDate()->format('Y-m-d');
            }
            if (isset($fields['regularSchedules'])) {
                $params['regularSchedules'] = $fields['regularSchedules'];
            } else {
                // create schedules
                $schedules = [];
                $days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
                foreach ($days as $num => $day) {
                    $this->treatDay($solidary->getProposal(), $num, $day, $schedules);
                }
                $params['regularSchedules'] = $schedules;
            }
        }

        $ad = $this->createAdFromArray($params, $this->getTimeAndMarginForStructure($newSolidary->getSolidaryUserStructure()->getStructure()));
        $newSolidary->setProposal($this->proposalRepository->find($ad->getId()));

        $this->solidaryTransportMatcher->match($newSolidary);

        // close original solidary
        $solidary->setStatus(Solidary::STATUS_CLOSED_FOR_EDITION);

        $this->entityManager->persist($solidary);
        $this->entityManager->persist($newSolidary);
        $this->entityManager->flush();

        // link potential outward and return solidaryMatchings that would have not been made yet (as the link between Matchings are made after the SolidaryMatchings)
        $this->solidaryMatchingRepository->linkRelatedSolidaryMatchings($newSolidary->getId());

        // send an event to warn that a SolidaryRecord has been created
        $event = new SolidaryCreatedEvent($this->poster, $newSolidary);
        $this->eventDispatcher->dispatch(SolidaryCreatedEvent::NAME, $event);

        // send an event to warn that the old SolidaryRecord has been deeply updated
        $event = new SolidaryDeeplyUpdated($this->poster, $solidary);
        $this->eventDispatcher->dispatch(SolidaryDeeplyUpdated::NAME, $event);

        return $this->getSolidary($newSolidary->getId());
        // $newProposal = clone $solidary->getProposal();
        // $newCriteria = clone $solidary->getProposal()->getCriteria();
        // $newProposal->setCriteria($newCriteria);
        // $newProposal->setSubject($newSolidary->getSubject());

        // if ($solidary->getProposal()->getProposalLinked()) {
        //     $newProposalLinked = clone $solidary->getProposal()->getProposalLinked();
        //     $newCriteriaLinked = clone $solidary->getProposal()->getProposalLinked()->getCriteria();
        //     $newProposalLinked->setCriteria($newCriteriaLinked);
        //     $newProposalLinked->setSubject($newSolidary->getSubject());
        //     $newProposal->setProposalLinked($newProposalLinked);
        // }

        // $newSolidary->setProposal($newProposal);

        // $this->entityManager->persist($newProposal);
        // $this->entityManager->persist($newSolidary);
        $this->entityManager->flush();

        return $newSolidary;
    }

    /**
     * Update a given beneficiary with the given array.
     *
     * @param User  $beneficiary  The beneficiary as a User
     * @param array $abeneficiary The array that contains the fields to update the properties of the beneficiary
     *
     * @return User The updated beneficiary
     */
    private function updateBeneficiary(User $beneficiary, array $abeneficiary)
    {
        // check if beneficiary informations have been updated
        if (isset($abeneficiary['givenName']) && $abeneficiary['givenName'] != $beneficiary->getGivenName()) {
            $beneficiary->setGivenName($abeneficiary['givenName']);
        }
        if (isset($abeneficiary['familyName']) && $abeneficiary['familyName'] != $beneficiary->getFamilyName()) {
            $beneficiary->setFamilyName($abeneficiary['familyName']);
        }
        if (isset($abeneficiary['email']) && $abeneficiary['email'] != $beneficiary->getEmail()) {
            $beneficiary->setEmail($abeneficiary['email']);
        }
        if (isset($abeneficiary['telephone']) && $abeneficiary['telephone'] != $beneficiary->getTelephone()) {
            $beneficiary->setTelephone($abeneficiary['telephone']);
        }
        if (isset($abeneficiary['gender']) && $abeneficiary['gender'] != $beneficiary->getGender()) {
            $beneficiary->setGender($abeneficiary['gender']);
        }
        if (isset($abeneficiary['birthDate']) && $abeneficiary['birthDate'] != $beneficiary->getBirthDate()) {
            $beneficiary->setBirthDate(new DateTime($abeneficiary['birthDate']));
        }
        if (isset($abeneficiary['newsSubscription']) && $abeneficiary['newsSubscription'] != $beneficiary->hasNewsSubscription()) {
            $beneficiary->setNewsSubscription($abeneficiary['newsSubscription']);
        }
        // check if beneficiary home address has been updated
        if (isset($abeneficiary['homeAddress'])) {
            // we search the original home address
            $homeAddress = null;
            foreach ($beneficiary->getAddresses() as $address) {
                if ($address->isHome()) {
                    $homeAddress = $address;

                    break;
                }
            }
            if (!is_null($homeAddress)) {
                // we have to update each field...
                /**
                 * @var Address $homeAddress
                 */
                $updated = false;
                if (isset($abeneficiary['homeAddress']['streetAddress']) && $homeAddress->getStreetAddress() != $abeneficiary['homeAddress']['streetAddress']) {
                    $updated = true;
                    $homeAddress->setStreetAddress($abeneficiary['homeAddress']['streetAddress']);
                }
                if (isset($abeneficiary['homeAddress']['postalCode']) && $homeAddress->getPostalCode() != $abeneficiary['homeAddress']['postalCode']) {
                    $updated = true;
                    $homeAddress->setPostalCode($abeneficiary['homeAddress']['postalCode']);
                }
                if (isset($abeneficiary['homeAddress']['addressLocality']) && $homeAddress->getAddressLocality() != $abeneficiary['homeAddress']['addressLocality']) {
                    $updated = true;
                    $homeAddress->setAddressLocality($abeneficiary['homeAddress']['addressLocality']);
                }
                if (isset($abeneficiary['homeAddress']['addressCountry']) && $homeAddress->getAddressCountry() != $abeneficiary['homeAddress']['addressCountry']) {
                    $updated = true;
                    $homeAddress->setAddressCountry($abeneficiary['homeAddress']['addressCountry']);
                }
                if (isset($abeneficiary['homeAddress']['latitude']) && $homeAddress->getLatitude() != $abeneficiary['homeAddress']['latitude']) {
                    $updated = true;
                    $homeAddress->setLatitude($abeneficiary['homeAddress']['latitude']);
                }
                if (isset($abeneficiary['homeAddress']['longitude']) && $homeAddress->getLongitude() != $abeneficiary['homeAddress']['longitude']) {
                    $updated = true;
                    $homeAddress->setLongitude($abeneficiary['homeAddress']['longitude']);
                }
                if (isset($abeneficiary['homeAddress']['houseNumber']) && $homeAddress->getHouseNumber() != $abeneficiary['homeAddress']['houseNumber']) {
                    $updated = true;
                    $homeAddress->setHouseNumber($abeneficiary['homeAddress']['houseNumber']);
                }
                if (isset($abeneficiary['homeAddress']['subLocality']) && $homeAddress->getSubLocality() != $abeneficiary['homeAddress']['subLocality']) {
                    $updated = true;
                    $homeAddress->setSubLocality($abeneficiary['homeAddress']['subLocality']);
                }
                if (isset($abeneficiary['homeAddress']['localAdmin']) && $homeAddress->getLocalAdmin() != $abeneficiary['homeAddress']['localAdmin']) {
                    $updated = true;
                    $homeAddress->setLocalAdmin($abeneficiary['homeAddress']['localAdmin']);
                }
                if (isset($abeneficiary['homeAddress']['county']) && $homeAddress->getCounty() != $abeneficiary['homeAddress']['county']) {
                    $updated = true;
                    $homeAddress->setCounty($abeneficiary['homeAddress']['county']);
                }
                if (isset($abeneficiary['homeAddress']['macroCounty']) && $homeAddress->getMacroCounty() != $abeneficiary['homeAddress']['macroCounty']) {
                    $updated = true;
                    $homeAddress->setMacroCounty($abeneficiary['homeAddress']['macroCounty']);
                }
                if (isset($abeneficiary['homeAddress']['region']) && $homeAddress->getRegion() != $abeneficiary['homeAddress']['region']) {
                    $updated = true;
                    $homeAddress->setRegion($abeneficiary['homeAddress']['region']);
                }
                if (isset($abeneficiary['homeAddress']['macroRegion']) && $homeAddress->getMacroRegion() != $abeneficiary['homeAddress']['macroRegion']) {
                    $updated = true;
                    $homeAddress->setMacroRegion($abeneficiary['homeAddress']['macroRegion']);
                }
                if (isset($abeneficiary['homeAddress']['countryCode']) && $homeAddress->getCountryCode() != $abeneficiary['homeAddress']['countryCode']) {
                    $updated = true;
                    $homeAddress->setCountryCode($abeneficiary['homeAddress']['countryCode']);
                }
                if ($updated) {
                    $this->entityManager->persist($homeAddress);
                }
            }
        }

        return $beneficiary;
    }

    /**
     * Add an animation to a solidary record.
     *
     * @param Solidary $solidary  The solidary to update
     * @param array    $animation The animation fields
     *
     * @return Solidary The solidary updated
     */
    private function addAnimation(Solidary $solidary, array $animation)
    {
        if (!array_key_exists('action', $animation)) {
            throw new SolidaryException(SolidaryException::SOLIDARY_ACTION_REQUIRED);
        }
        if (!array_key_exists('user', $animation)) {
            throw new SolidaryException(SolidaryException::SOLIDARY_ACTION_USER_REQUIRED);
        }
        if (!$action = $this->actionRepository->find($animation['action'])) {
            throw new SolidaryException(sprintf(SolidaryException::SOLIDARY_ACTION_NOT_FOUND, $animation['action']));
        }
        if (!$user = $this->userRepository->find($animation['user'])) {
            throw new SolidaryException(sprintf(SolidaryException::SOLIDARY_ACTION_USER_NOT_FOUND, $animation['user']));
        }
        $newAnimation = new Animation();
        $newAnimation->setSolidary($solidary);
        $newAnimation->setAction($action);
        $newAnimation->setUser($user);
        $newAnimation->setAuthor($this->poster);
        if (array_key_exists('comment', $animation)) {
            $newAnimation->setComment(nl2br(strip_tags($animation['comment'])));
        }
        // if (array_key_exists('message', $animation)) {
        //     // there's a message associated with the animation, we need to build a Message object
        //     $message = new Message();
        //     $message->setText($animation['message']);
        //     if (array_key_exists('messageDelegated', $animation) && $animation['messageDelegated'] == 1) {
        //         // message sent as delegated ('in the name of')
        //         $message->setUser($solidary->getSolidaryUserStructure()->getSolidaryUser()->getUser());
        //         $message->setUserDelegate($this->poster);
        //     } else {
        //         $message->setUser($this->poster);
        //     }
        //     $newAnimation->setMessage($message);
        // }
        if (array_key_exists('progression', $animation)) {
            if ($animation['progression'] > 0) {
                $newAnimation->setProgression($animation['progression']);
            }
        }
        // send event to warn that an animation has been made
        $event = new AnimationMadeEvent($newAnimation);
        $this->eventDispatcher->dispatch(AnimationMadeEvent::NAME, $event);

        $persist = false;
        // check again for progression to set the solidary progression
        if (array_key_exists('progression', $animation)) {
            if ($animation['progression'] > 0) {
                $solidary->setProgression($animation['progression']);
                $persist = true;
                // special case, manual update of progression
                if (Action::ACTION_SOLIDARY_UPDATE_PROGRESS_MANUALLY == $action->getId()) {
                    $solidary->setStatus(Solidary::STATUS_PROGRESSION[(int) $animation['progression']]);
                }
            }
        }

        // update the status of the solidary record
        if ($action->getType() && in_array($action->getType(), Solidary::STATUSES)) {
            $solidary->setStatus($action->getType());
            $persist = true;
        }

        if ($persist) {
            $this->entityManager->persist($solidary);
            $this->entityManager->flush();
        }

        // we don't go further, the event subscribers have done the job to persist the data, although we need a complete solidary !
        return $this->getSolidary($solidary->getId());
    }

    /**
     * Add a solution to a solidary record.
     *
     * @param Solidary $solidary The solidary to update
     * @param array    $solution The solution fields
     *
     * @return Solidary The solidary updated
     */
    private function addSolution(Solidary $solidary, array $solution)
    {
        if (!array_key_exists('matching', $solution)) {
            throw new SolidaryException(SolidaryException::SOLIDARY_SOLUTION_MATCHING_REQUIRED);
        }
        if (!$solidaryMatching = $this->solidaryMatchingRepository->find($solution['matching'])) {
            throw new SolidaryException(sprintf(SolidaryException::SOLIDARY_SOLUTION_MATCHING_NOT_FOUND, $solution['matching']));
        }
        if (!array_key_exists('carpooler', $solution) && !array_key_exists('transporter', $solution)) {
            throw new SolidaryException(SolidaryException::SOLIDARY_SOLUTION_ROLE_REQUIRED);
        }
        if (array_key_exists('carpooler', $solution) && !$user = $this->userRepository->find($solution['carpooler'])) {
            throw new SolidaryException(sprintf(SolidaryException::SOLIDARY_SOLUTION_USER_NOT_FOUND, $solution['carpooler']));
        }
        if (array_key_exists('transporter', $solution) && !$user = $this->userRepository->find($solution['transporter'])) {
            throw new SolidaryException(sprintf(SolidaryException::SOLIDARY_SOLUTION_USER_NOT_FOUND, $solution['transporter']));
        }

        // create solution
        $solidarySolution = new SolidarySolution();
        $solidarySolution->setSolidaryMatching($solidaryMatching);
        $solidarySolution->setSolidary($solidaryMatching->getSolidary());
        $solidary->addSolidarySolution($solidarySolution);
        $this->entityManager->persist($solidarySolution);

        // if we choose to auto link (= automatically link outward and return)
        if ($solidaryMatching->getSolidaryMatchingLinked()) {
            $solidarySolutionLinked = new SolidarySolution();
            $solidarySolutionLinked->setSolidaryMatching($solidaryMatching->getSolidaryMatchingLinked());
            $solidarySolutionLinked->setSolidary($solidaryMatching->getSolidary());
            $solidary->addSolidarySolution($solidarySolutionLinked);
            $this->entityManager->persist($solidarySolutionLinked);
        }

        // if no auto link, uncomment to link possible outward/return solution
        // foreach ($solidaryMatching->getSolidary()->getSolidarySolutions() as $solution) {
        //     /**
        //      * @var SolidarySolution $solution
        //      */
        //     if ($solution->getSolidaryMatching()->getSolidaryMatchingLinked() &&  $solution->getSolidaryMatching()->getSolidaryMatchingLinked()->getId() == $solidaryMatching->getId()) {
        //         $solidarySolution->setSolidarySolutionLinked($solution);
        //     }
        // }

        $this->entityManager->flush();

        // we need a complete solidary
        return $this->getSolidary($solidary->getId());
    }

    /**
     * Add a message to a solidary record.
     *
     * @param Solidary $solidary The solidary to update
     * @param array    $message  The message fields
     *
     * @return Solidary The solidary updated
     */
    private function addMessage(Solidary $solidary, array $message)
    {
        if (!array_key_exists('solution', $message)) {
            throw new SolidaryException(SolidaryException::SOLIDARY_MESSAGE_SOLUTION_REQUIRED);
        }
        if (!$solidarySolution = $this->solidarySolutionRepository->find($message['solution'])) {
            throw new SolidaryException(sprintf(SolidaryException::SOLIDARY_MESSAGE_SOLUTION_NOT_FOUND, $message['solution']));
        }
        if (!array_key_exists('text', $message)) {
            throw new SolidaryException(SolidaryException::SOLIDARY_MESSAGE_TEXT_REQUIRED);
        }

        // create new message
        // first we check if there's already a solidaryAsk related with the solution
        $solidaryAsk = $solidarySolution->getSolidaryAsk();
        $firstMessage = null;
        if (!$solidaryAsk) {
            // create the solidaryAsk
            $solidaryAsk = new SolidaryAsk();
            $solidaryAsk->setStatus(SolidaryAsk::STATUS_ASKED);
            $solidaryAsk->setSolidarySolution($solidarySolution);
            // uncomment and complete for full mode
            // if ($solidarySolution->getSolidaryMatching()->getMatching()) {
            //     // the solution is a carpooler, we need to create an associated Ask
            //     $ask = new Ask();
            //     ...
            //     $solidaryAsk->setAsk($ask);
            // }
            $this->entityManager->persist($solidaryAsk);
        } else {
            $firstMessage = $this->messageRepository->findFirstForSolidaryAsk($solidaryAsk);
        }

        // create the message and recipient
        $newMessage = new Message();
        $newMessage->setText(nl2br(strip_tags($message['text'])));
        $newMessage->setUserDelegate($this->poster);
        $newMessage->setUser($solidarySolution->getSolidary()->getSolidaryUserStructure()->getSolidaryUser()->getUser());
        if ($firstMessage) {
            $newMessage->setMessage($firstMessage);
        }
        $recipient = new Recipient();
        $recipient->setStatus(Recipient::STATUS_PENDING);
        if ($solidarySolution->getSolidaryMatching()->getMatching()) {
            // the solution is a carpooler
            $recipient->setUser($solidarySolution->getSolidaryMatching()->getMatching()->getProposalOffer()->getUser());
        } else {
            // the solution is a volunteer
            $recipient->setUser($solidarySolution->getSolidaryMatching()->getSolidaryUser()->getUser());
        }
        $newMessage->addRecipient($recipient);

        // create the solidaryAskHistory
        $solidaryAskHistory = new SolidaryAskHistory();
        $solidaryAskHistory->setStatus($solidaryAsk->getStatus());
        $solidaryAskHistory->setMessage($newMessage);
        $solidaryAsk->addSolidaryAskHistory($solidaryAskHistory);

        $this->entityManager->persist($solidaryAskHistory);
        $this->entityManager->persist($newMessage);
        $this->entityManager->flush();

        // we need a complete solidary
        return $this->getSolidary($solidarySolution->getSolidary()->getId());
    }

    /**
     * Update proofs for a solidary record.
     *
     * @param Solidary $solidary The solidary to update
     * @param array    $proofs   The proof fields
     */
    private function updateProofs(Solidary $solidary, array $proofs)
    {
        foreach ($proofs as $aproof) {
            if (!isset($aproof['id'])) {
                throw new SolidaryException(SolidaryException::STRUCTURE_PROOF_ID_REQUIRED);
            }
            // if (!isset($aproof['value'])) {
            //     throw new SolidaryException(sprintf(SolidaryException::STRUCTURE_PROOF_VALUE_REQUIRED, $proof['id']));
            // }
            if (!$structureProof = $this->structureProofRepository->find($aproof['id'])) {
                throw new SolidaryException(sprintf(SolidaryException::STRUCTURE_PROOF_NOT_FOUND, $aproof['id']));
            }
            if (is_null($aproof['value']) && $structureProof->isMandatory() && !$structureProof->isFile()) {
                throw new SolidaryException(sprintf(SolidaryException::STRUCTURE_PROOF_VALUE_REQUIRED, $aproof['id']));
            }
            // we check for each proof if it's a new proof or an updated one
            $found = false;
            foreach ($solidary->getSolidaryUserStructure()->getProofs() as $proof) {
                /**
                 * @var Proof $proof
                 */
                if ($proof->getStructureProof()->getId() == $aproof['id']) {
                    // updated proof
                    $found = true;
                    if (!is_null($proof->getValue()) && is_null($aproof['value']) && !$structureProof->isFile()) {
                        // the proof was deleted, and it's not a file
                        $solidary->getSolidaryUserStructure()->removeProof($proof);
                        $this->entityManager->persist($solidary);
                    } elseif (is_null($proof->getValue()) && !isset($aproof['fileName']) && $structureProof->isFile()) {
                        // the proof was deleted, and it's a file
                        $solidary->getSolidaryUserStructure()->removeProof($proof);
                        $this->entityManager->persist($solidary);
                    } elseif (!is_null($aproof['value']) && $aproof['value'] !== $proof->getValue()) {
                        // the proof was updated
                        $proof->setValue($aproof['value']);
                        $this->entityManager->persist($proof);
                    }

                    break;
                }
            }
            if (!$found && !$structureProof->isFile()) {
                // new proof (only for non files)
                $proof = new Proof();
                $proof->setStructureProof($structureProof);
                $proof->setValue($aproof['value']);
                $solidary->getSolidaryUserStructure()->addProof($proof);
                $this->entityManager->persist($proof);
            }
        }
    }

    /**
     * Update needs for a solidary record.
     *
     * @param Solidary         $solidary       The solidary to update
     * @param null|array       $needs          The need fields
     * @param null|bool|string $additionalNeed A potential additional need
     */
    private function updateNeeds(Solidary $solidary, ?array $needs = null, $additionalNeed = false)
    {
        $existingAdditionalNeed = false;

        // check for removed needs
        foreach ($solidary->getNeeds() as $need) {
            /**
             * @var Need $need
             */
            if (!$need->isPrivate() && is_array($needs) && !in_array($need->getId(), $needs)) {
                $solidary->removeNeed($need);
            } elseif ($need->isPrivate() && (is_null($additionalNeed) || '' === $additionalNeed)) {
                $solidary->removeNeed($need);
                $this->entityManager->remove($need);
            } elseif ($need->isPrivate()) {
                $existingAdditionalNeed = $need;
            }
        }

        // check for added needs
        if (is_array($needs)) {
            foreach ($needs as $aneed) {
                if ($need = $this->needRepository->find($aneed)) {
                    // check if need already exists in the solidary record
                    $found = false;
                    foreach ($solidary->getNeeds() as $sneed) {
                        /**
                         * @var Need $sneed
                         */
                        if ($sneed->getId() === $need->getId()) {
                            $found = true;

                            break;
                        }
                    }
                    if (!$found) {
                        $solidary->addNeed($need);
                    }
                } else {
                    throw new SolidaryException(sprintf(SolidaryException::NEED_NOT_FOUND, $need));
                }
            }
        }

        // check for additional need
        if (!is_null($additionalNeed) && '' !== $additionalNeed && false !== $additionalNeed) {
            if (!$existingAdditionalNeed) {
                $need = new Need();
                $need->setLabel($additionalNeed);
                $need->setPrivate(true);
                // set the solidary as origin
                $need->setSolidary($solidary);
                $this->entityManager->persist($need);
                $solidary->addNeed($need);
            } elseif ($existingAdditionalNeed->getLabel() !== $additionalNeed) {
                $existingAdditionalNeed->setLabel($additionalNeed);
                $this->entityManager->persist($need);
            }
        }
    }

    /**
     * Create an Ad from an array.
     *
     * The array can contain the following informations :
     * - origin (mandatory)
     * - destination (mandatory, value can be null though)
     * - regular (T/F)
     * - punctualOutwardDateChoice :    1 = chosen outward date, 2 = in the next 7 days, 3 = in the next 15 days, 4 = in the next 30 days
     * - punctualOutwardTimeChoice :    1 = chosen outward time, 2 = between mMin and mMax, 3 = between aMin and aMax, 4 = between eMin and eMax
     * - punctualOutwardMinDate :       computed outward min date
     * - punctualOutwardMinTime :       computed outward min time
     * - punctualOutwardMaxDate :       computed outward max date
     * - punctualReturnDateChoice :     1 = no return, 2 = one hour later, 3 = 2 hours later, 4 = 3 hours later, 5 = chosen return date and time
     * - punctualReturnDate :           computed return date
     * - punctualReturnTime :           computed return time
     * - regularMinDate :               chosen regular min date
     * - regularDateChoice :            1 = for a week, 2 = for a month, 3 = till a given date
     * - regularMaxDate :               computed regular max date
     * - regularSchedules [
     *      days [
     *          1,2,3...7
     *      ],
     *      outwardTime,
     *      returnTime
     * ]
     *
     * @param array $aad   The ad informations as an array
     * @param array $times The time ranges (depends on the structure)
     *
     * @return Ad The Ad object
     */
    private function createAdFromArray(array $aad, array $times): Ad
    {
        // foreach ($aad as $key=>$value) {
        //     if (substr($key,0,7) == "regular" || substr($key,0,8) == "punctual") {
        //         echo $key . PHP_EOL;
        //         var_dump($value);
        //     }
        // }
        // exit;
        $ad = new Ad();

        // users
        $ad->setPosterId($aad['poster']);
        $ad->setUserId($aad['beneficiary']);

        // subject
        $ad->setSubjectId($aad['subject']);

        // origin & destination
        $origin = new Address();
        $destination = null;

        if (isset($aad['origin']['houseNumber'])) {
            $origin->setHouseNumber($aad['origin']['houseNumber']);
        }
        if (isset($aad['origin']['street'])) {
            $origin->setStreet($aad['origin']['street']);
        }
        if (isset($aad['origin']['streetAddress'])) {
            $origin->setStreetAddress($aad['origin']['streetAddress']);
        }
        if (isset($aad['origin']['postalCode'])) {
            $origin->setPostalCode($aad['origin']['postalCode']);
        }
        if (isset($aad['origin']['subLocality'])) {
            $origin->setSubLocality($aad['origin']['subLocality']);
        }
        if (isset($aad['origin']['addressLocality'])) {
            $origin->setAddressLocality($aad['origin']['addressLocality']);
        }
        if (isset($aad['origin']['localAdmin'])) {
            $origin->setLocalAdmin($aad['origin']['localAdmin']);
        }
        if (isset($aad['origin']['county'])) {
            $origin->setCounty($aad['origin']['county']);
        }
        if (isset($aad['origin']['macroCounty'])) {
            $origin->setMacroCounty($aad['origin']['macroCounty']);
        }
        if (isset($aad['origin']['region'])) {
            $origin->setRegion($aad['origin']['region']);
        }
        if (isset($aad['origin']['macroRegion'])) {
            $origin->setMacroRegion($aad['origin']['macroRegion']);
        }
        if (isset($aad['origin']['addressCountry'])) {
            $origin->setAddressCountry($aad['origin']['addressCountry']);
        }
        if (isset($aad['origin']['countryCode'])) {
            $origin->setCountryCode($aad['origin']['countryCode']);
        }
        if (isset($aad['origin']['latitude'])) {
            $origin->setLatitude($aad['origin']['latitude']);
        }
        if (isset($aad['origin']['longitude'])) {
            $origin->setLongitude($aad['origin']['longitude']);
        }

        if ($aad['destination']) {
            $destination = new Address();
            if (isset($aad['destination']['houseNumber'])) {
                $destination->setHouseNumber($aad['destination']['houseNumber']);
            }
            if (isset($aad['destination']['street'])) {
                $destination->setStreet($aad['destination']['street']);
            }
            if (isset($aad['destination']['streetAddress'])) {
                $destination->setStreetAddress($aad['destination']['streetAddress']);
            }
            if (isset($aad['destination']['postalCode'])) {
                $destination->setPostalCode($aad['destination']['postalCode']);
            }
            if (isset($aad['destination']['subLocality'])) {
                $destination->setSubLocality($aad['destination']['subLocality']);
            }
            if (isset($aad['destination']['addressLocality'])) {
                $destination->setAddressLocality($aad['destination']['addressLocality']);
            }
            if (isset($aad['destination']['localAdmin'])) {
                $destination->setLocalAdmin($aad['destination']['localAdmin']);
            }
            if (isset($aad['destination']['county'])) {
                $destination->setCounty($aad['destination']['county']);
            }
            if (isset($aad['destination']['macroCounty'])) {
                $destination->setMacroCounty($aad['destination']['macroCounty']);
            }
            if (isset($aad['destination']['region'])) {
                $destination->setRegion($aad['destination']['region']);
            }
            if (isset($aad['destination']['macroRegion'])) {
                $destination->setMacroRegion($aad['destination']['macroRegion']);
            }
            if (isset($aad['destination']['addressCountry'])) {
                $destination->setAddressCountry($aad['destination']['addressCountry']);
            }
            if (isset($aad['destination']['countryCode'])) {
                $destination->setCountryCode($aad['destination']['countryCode']);
            }
            if (isset($aad['destination']['latitude'])) {
                $destination->setLatitude($aad['destination']['latitude']);
            }
            if (isset($aad['destination']['longitude'])) {
                $destination->setLongitude($aad['destination']['longitude']);
            }
        } else {
            $ad->setNoDestination(true);
            $destination = clone $origin;
        }

        $ad->setOutwardWaypoints([clone $origin, clone $destination]);
        $ad->setReturnWaypoints([clone $destination, clone $origin]);

        // role
        $ad->setRole(Ad::ROLE_PASSENGER);

        // we set the ad as a solidary ad
        $ad->setSolidary(true);
        $ad->setSolidaryRecord($aad['solidary']);

        // initialize to one way for now
        $ad->setOneWay(true);

        // frequency set to punctual for now
        $ad->setFrequency(Criteria::FREQUENCY_PUNCTUAL);
        if ($aad['regular']) {
            if (!isset($aad['regularMinDate'])) {
                throw new SolidaryException(SolidaryException::REGULAR_MIN_DATE_REQUIRED);
            }
            if (!isset($aad['regularMaxDate'])) {
                throw new SolidaryException(SolidaryException::REGULAR_MAX_DATE_REQUIRED);
            }
            $ad->setFrequency(Criteria::FREQUENCY_REGULAR);
            $ad->setOutwardDate(new DateTime($aad['regularMinDate']));
            $ad->setOutwardLimitDate(new DateTime($aad['regularMaxDate']));
            $ad->setSchedule($aad['regularSchedules']);
            // check if there are returns
            $return = false;
            foreach ($aad['regularSchedules'] as $schedule) {
                if ($schedule['returnTime']) {
                    $return = true;

                    break;
                }
            }
            if ($return) {
                $ad->setOneWay(false);
                $ad->setReturnDate(new DateTime($aad['regularMinDate']));
                $ad->setReturnLimitDate(new DateTime($aad['regularMaxDate']));
            }
        } else {
            $ad->setStrictDate(true);
            $ad->setOutwardDate(new DateTime($aad['punctualOutwardMinDate']));
            $schedule = null;

            switch ($aad['punctualOutwardDateChoice']) {
                case Solidary::PUNCTUAL_OUTWARD_DATE_CHOICE_DATE:
                    break;

                case Solidary::PUNCTUAL_OUTWARD_DATE_CHOICE_7:
                case Solidary::PUNCTUAL_OUTWARD_DATE_CHOICE_15:
                case Solidary::PUNCTUAL_OUTWARD_DATE_CHOICE_30:
                    // transform to regular trip as the date is flexible
                    $ad->setFrequency(Criteria::FREQUENCY_REGULAR);
                    $schedule = ['mon' => true, 'tue' => true, 'wed' => true, 'thu' => true, 'fri' => true, 'sat' => true, 'sun' => true];
                    $ad->setOutwardLimitDate(new DateTime($aad['punctualOutwardMaxDate']));

                    break;
            }

            switch ($aad['punctualOutwardTimeChoice']) {
                case Solidary::PUNCTUAL_TIME_CHOICE_TIME:
                    if (Criteria::FREQUENCY_PUNCTUAL == $ad->getFrequency()) {
                        $ad->setOutwardTime($aad['punctualOutwardMinTime']);
                    } else {
                        $schedule['outwardTime'] = $aad['punctualOutwardMinTime'];
                    }

                    break;

                case Solidary::PUNCTUAL_TIME_CHOICE_M:
                    if (Criteria::FREQUENCY_PUNCTUAL == $ad->getFrequency()) {
                        $ad->setOutwardTime($times['mTime']);
                    } else {
                        $schedule['outwardTime'] = $times['mTime'];
                    }
                    $ad->setMarginduration($times['mMargin']);

                    break;

                case Solidary::PUNCTUAL_TIME_CHOICE_A:
                    if (Criteria::FREQUENCY_PUNCTUAL == $ad->getFrequency()) {
                        $ad->setOutwardTime($times['aTime']);
                    } else {
                        $schedule['outwardTime'] = $times['aTime'];
                    }
                    $ad->setMarginduration($times['aMargin']);

                    break;

                case Solidary::PUNCTUAL_TIME_CHOICE_E:
                    if (Criteria::FREQUENCY_PUNCTUAL == $ad->getFrequency()) {
                        $ad->setOutwardTime($times['eTime']);
                    } else {
                        $schedule['outwardTime'] = $times['eTime'];
                    }
                    $ad->setMarginduration($times['eMargin']);

                    break;
            }

            switch ($aad['punctualReturnDateChoice']) {
                case Solidary::PUNCTUAL_RETURN_DATE_CHOICE_NULL:
                    break;

                case Solidary::PUNCTUAL_RETURN_DATE_CHOICE_1:
                    $ad->setOneWay(false);
                    // add 1 hour to outward time
                    $now = new DateTime();
                    if (Criteria::FREQUENCY_PUNCTUAL == $ad->getFrequency()) {
                        $now->setTime((int) substr($ad->getOutwardTime(), 0, 2), (int) substr($ad->getOutwardTime(), 3, 2));
                        $now->add(new DateInterval('PT1H'));
                        $ad->setReturnTime($now->format('H:i'));
                        $ad->setReturnDate($ad->getOutwardDate());
                    } else {
                        $now->setTime((int) substr($schedule['outwardTime'], 0, 2), (int) substr($schedule['outwardTime'], 3, 2));
                        $now->add(new DateInterval('PT1H'));
                        $schedule['returnTime'] = $now->format('H:i');
                    }

                    break;

                case Solidary::PUNCTUAL_RETURN_DATE_CHOICE_2:
                    $ad->setOneWay(false);
                    // add 2 hours to outward time
                    $now = new DateTime();
                    if (Criteria::FREQUENCY_PUNCTUAL == $ad->getFrequency()) {
                        $now->setTime((int) substr($ad->getOutwardTime(), 0, 2), (int) substr($ad->getOutwardTime(), 3, 2));
                        $now->add(new DateInterval('PT2H'));
                        $ad->setReturnTime($now->format('H:i'));
                        $ad->setReturnDate($ad->getOutwardDate());
                    } else {
                        $now->setTime((int) substr($schedule['outwardTime'], 0, 2), (int) substr($schedule['outwardTime'], 3, 2));
                        $now->add(new DateInterval('PT2H'));
                        $schedule['returnTime'] = $now->format('H:i');
                    }

                    break;

                case Solidary::PUNCTUAL_RETURN_DATE_CHOICE_3:
                    $ad->setOneWay(false);
                    // add 3 hours to outward time
                    $now = new DateTime();
                    if (Criteria::FREQUENCY_PUNCTUAL == $ad->getFrequency()) {
                        $now->setTime((int) substr($ad->getOutwardTime(), 0, 2), (int) substr($ad->getOutwardTime(), 3, 2));
                        $now->add(new DateInterval('PT3H'));
                        $ad->setReturnTime($now->format('H:i'));
                        $ad->setReturnDate($ad->getOutwardDate());
                    } else {
                        $now->setTime((int) substr($schedule['outwardTime'], 0, 2), (int) substr($schedule['outwardTime'], 3, 2));
                        $now->add(new DateInterval('PT3H'));
                        $schedule['returnTime'] = $now->format('H:i');
                    }

                    break;

                case Solidary::PUNCTUAL_RETURN_DATE_CHOICE_DATE:
                    $ad->setOneWay(false);
                    // chosen return date and time => only punctual
                    $ad->setReturnDate(new DateTime($aad['punctualReturnDate']));
                    $ad->setReturnTime($aad['punctualReturnTime']);

                    break;
            }
            if (Criteria::FREQUENCY_REGULAR == $ad->getFrequency()) {
                $ad->setSchedule([$schedule]);
            }
        }

        // do not retrieve results to avoid unwanted side effects (as results can require data that is not present / not well formed on the current ad, eg. flexible ads)
        return $this->adManager->createAd($ad, true, true, false);
    }

    private function getTimeAndMarginForStructure(Structure $structure)
    {
        $now = new Datetime();
        $mMinTime = clone $now;
        $mMaxTime = clone $now;
        $aMinTime = clone $now;
        $aMaxTime = clone $now;
        $eMinTime = clone $now;
        $eMaxTime = clone $now;
        $mMinTime->setTime($structure->getMMinTime()->format('H'), $structure->getMMinTime()->format('i'));
        $mMaxTime->setTime($structure->getMMaxTime()->format('H'), $structure->getMMaxTime()->format('i'));
        $aMinTime->setTime($structure->getAMinTime()->format('H'), $structure->getAMinTime()->format('i'));
        $aMaxTime->setTime($structure->getAMaxTime()->format('H'), $structure->getAMaxTime()->format('i'));
        $eMinTime->setTime($structure->getEMinTime()->format('H'), $structure->getEMinTime()->format('i'));
        $eMaxTime->setTime($structure->getEMaxTime()->format('H'), $structure->getEMaxTime()->format('i'));
        $mMargin = ($mMaxTime->getTimestamp() - $mMinTime->getTimestamp()) / 2;
        $aMargin = ($aMaxTime->getTimestamp() - $aMinTime->getTimestamp()) / 2;
        $eMargin = ($eMaxTime->getTimestamp() - $eMinTime->getTimestamp()) / 2;

        return [
            'mTime' => $mMinTime->add(new DateInterval('PT'.$mMargin.'S'))->format('H:i'),
            'aTime' => $aMinTime->add(new DateInterval('PT'.$aMargin.'S'))->format('H:i'),
            'eTime' => $eMinTime->add(new DateInterval('PT'.$eMargin.'S'))->format('H:i'),
            'mMargin' => $mMargin,
            'aMargin' => $aMargin,
            'eMargin' => $eMargin,
        ];
    }
}