Covivo/mobicoop

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

Summary

Maintainability
D
2 days
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\Carpool\Service;

use App\Carpool\Entity\AntiFraudResponse;
use App\Carpool\Entity\Criteria;
use App\Carpool\Entity\Proposal;
use App\Carpool\Exception\AntiFraudException;
use App\Carpool\Repository\ProposalRepository;
use App\Carpool\Repository\WaypointRepository;
use App\Carpool\Ressource\Ad;
use App\Geography\Entity\Address;
use App\Geography\Service\GeoRouter;
use App\User\Service\UserManager;
use DateInterval;
use DateTime;

/**
 * Anti-Fraud system manager service.
 *
 * @author Maxime Bardot <maxime.bardot@mobicoop.org>
 */
class AntiFraudManager
{
    private $geoRouter;
    private $proposalRepository;
    private $userManager;
    private $waypointRepository;

    // Parameters
    private $distanceMinCheck;
    private $nbCarpoolsMax;
    private $active;

    private $sameDayProposals;

    /**
     * Constructor.
     *
     * @param EntityManagerInterface $entityManager
     */
    public function __construct(
        GeoRouter $geoRouter,
        ProposalRepository $proposalRepository,
        WaypointRepository $waypointRepository,
        UserManager $userManager,
        array $params
    ) {
        $this->geoRouter = $geoRouter;
        $this->proposalRepository = $proposalRepository;
        $this->waypointRepository = $waypointRepository;
        $this->userManager = $userManager;
        $this->distanceMinCheck = $params['distanceMinCheck'];
        $this->nbCarpoolsMax = $params['nbCarpoolsMax'];
        $this->active = $params['active'];
    }

    /**
     * Check if an Ad is valid against the Anti-Fraud system rules
     * If the Anti-Fraud system is inactive, or the Ad is a Search or the role is passenger only, it's an automatic validation.
     *
     * @param Ad $ad The Ad to check
     *
     * @return AntiFraudResponse The response
     */
    public function validAd(Ad $ad, bool $unpausedAd = false): AntiFraudResponse
    {
        // Default response is that the Ad is valid
        $response = new AntiFraudResponse(true, AntiFraudException::OK);

        // If the Anti-Fraud system is inactive, or the Ad is a Search or the role is passenger only, it's an automatic validation
        if (!$this->active || Ad::ROLE_PASSENGER == $ad->getRole() || $ad->isSearch()) {
            return $response;
        }

        // Compute the distance of the journey

        $addressesToValidate = [];
        foreach ($ad->getOutwardWaypoints() as $pointToValidate) {
            $waypointToValidate = new Address();
            if (is_array($pointToValidate)) {
                $waypointToValidate->setLatitude(isset($pointToValidate['latitude']) ? $pointToValidate['latitude'] : $pointToValidate['address']['latitude']);
                $waypointToValidate->setLongitude(isset($pointToValidate['longitude']) ? $pointToValidate['longitude'] : $pointToValidate['address']['longitude']);
            } else {
                $waypointToValidate->setLatitude($pointToValidate->getLatitude());
                $waypointToValidate->setLongitude($pointToValidate->getLongitude());
            }
            $addressesToValidate[] = $waypointToValidate;
        }

        $route = $this->geoRouter->getRoutes($addressesToValidate, false, true);
        // var_dump($route);

        // If the journey is above the $distanceMinCheck paramaters we need to check it otherwise, it's an immediate validation
        if (count($route) > 0 && $route[0] && ($route[0]->getDistance() / 1000) > $this->distanceMinCheck) {
            // FIRST CHECK
            $response = $this->validAdFirstCheck($ad);
            if (!$response->isValid()) {
                // we check if the arrival date is after the start date of the proposal we are trying to post or unpaused on the same day
                foreach ($this->sameDayProposals as $sameDayProposal) {
                    if ($ad->getProposalId() == $sameDayProposal->getId()) {
                        // In case of adding a return, we avoid checkin the Ad against the outward proposal
                        continue;
                    }
                    if ($unpausedAd) {
                        $response = $this->checkValidHoursUnpausedAd($ad, $sameDayProposal);
                        if (!$response->isValid()) {
                            return $response;
                        }
                        $response = $this->checkTravelCanBeMadeUnpausedAd($ad, $sameDayProposal);
                        if (!$response->isValid()) {
                            return $response;
                        }
                    } else {
                        $response = $this->checkValidHours($ad, $sameDayProposal);
                        if (!$response->isValid()) {
                            return $response;
                        }
                        $response = $this->checkTravelCanBeMade($ad, $sameDayProposal);
                        if (!$response->isValid()) {
                            return $response;
                        }
                    }
                }
            }
        }

        return $response;
    }

    public function checkTravelCanBeMadeUnpausedAd(Ad $ad, Proposal $sameDayProposal): AntiFraudResponse
    {
        $adAddressOrigin = new Address();
        $adAddressOrigin->setLatitude($ad->getOutwardWaypoints()[0]['address']['latitude']);
        $adAddressOrigin->setLongitude($ad->getOutwardWaypoints()[0]['address']['longitude']);

        $proposalWaypointDestination = $this->waypointRepository->findOneBy(['proposal' => $sameDayProposal, 'destination' => 1]);

        $ODToCheck = [$proposalWaypointDestination->getAddress(), $adAddressOrigin];

        $routeBetweenProposalDestinationAndAdOrigin = $this->geoRouter->getRoutes($ODToCheck, false, true);
        $travelDurationBetweenProposalDestinationAndAdOrigin = $routeBetweenProposalDestinationAndAdOrigin[0]->getDuration();

        $days = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'];

        foreach ($days as $day) {
            $schedule = $ad->getSchedule();
            if (isset($schedule[$day]) && $schedule[$day]) {
                $adOriginDateTime = new \DateTime('now');

                switch ($day) {
                    case 'sun':
                        $adOriginDateTime = $schedule['sunOutwardTime'];
                        $arrivalHourToNextOrigin = $sameDayProposal->getCriteria()->getArrivalSunTime()->add(new DateInterval('PT'.$travelDurationBetweenProposalDestinationAndAdOrigin.'S'));

                        break;

                    case 'mon':
                        $adOriginDateTime = $schedule['monOutwardTime'];
                        $arrivalHourToNextOrigin = $sameDayProposal->getCriteria()->getArrivalMonTime()->add(new DateInterval('PT'.$travelDurationBetweenProposalDestinationAndAdOrigin.'S'));

                        break;

                    case 'tue':
                        $adOriginDateTime = $schedule['tueOutwardTime'];
                        $arrivalHourToNextOrigin = $sameDayProposal->getCriteria()->getArrivalTueTime()->add(new DateInterval('PT'.$travelDurationBetweenProposalDestinationAndAdOrigin.'S'));

                        break;

                    case 'wed':
                        $adOriginDateTime = $schedule['wedOutwardTime'];
                        $arrivalHourToNextOrigin = $sameDayProposal->getCriteria()->getArrivalWedTime()->add(new DateInterval('PT'.$travelDurationBetweenProposalDestinationAndAdOrigin.'S'));

                        break;

                    case 'thu':
                        $adOriginDateTime = $schedule['thuOutwardTime'];
                        $arrivalHourToNextOrigin = $sameDayProposal->getCriteria()->getArrivalThuTime()->add(new DateInterval('PT'.$travelDurationBetweenProposalDestinationAndAdOrigin.'S'));

                        break;

                    case 'fri':
                        $adOriginDateTime = $schedule['friOutwardTime'];
                        $arrivalHourToNextOrigin = $sameDayProposal->getCriteria()->getArrivalFriTime()->add(new DateInterval('PT'.$travelDurationBetweenProposalDestinationAndAdOrigin.'S'));

                        break;

                    case 'sat':
                        $adOriginDateTime = $schedule['satOutwardTime'];
                        $arrivalHourToNextOrigin = $sameDayProposal->getCriteria()->getArrivalSatTime()->add(new DateInterval('PT'.$travelDurationBetweenProposalDestinationAndAdOrigin.'S'));

                        break;
                }

                if (!is_null($arrivalHourToNextOrigin)) {
                    if ($adOriginDateTime <= $arrivalHourToNextOrigin) {
                        return new AntiFraudResponse(false, AntiFraudException::NOT_ENOUGH_TIME);
                    }
                }
            }
        }

        return new AntiFraudResponse(true, AntiFraudException::OK);
    }

    public function checkTravelCanBeMade(Ad $ad, Proposal $sameDayProposal): AntiFraudResponse
    {
        $adAddressOrigin = new Address();
        $adAddressOrigin->setLatitude($ad->getOutwardWaypoints()[0]['latitude']);
        $adAddressOrigin->setLongitude($ad->getOutwardWaypoints()[0]['longitude']);

        $proposalWaypointDestination = $this->waypointRepository->findOneBy(['proposal' => $sameDayProposal, 'destination' => 1]);

        $ODToCheck = [$proposalWaypointDestination->getAddress(), $adAddressOrigin];

        $routeBetweenProposalDestinationAndAdOrigin = $this->geoRouter->getRoutes($ODToCheck, false, true);
        $travelDurationBetweenProposalDestinationAndAdOrigin = $routeBetweenProposalDestinationAndAdOrigin[0]->getDuration();

        // punctual ad match punctual ad
        if (Criteria::FREQUENCY_PUNCTUAL == $ad->getFrequency() && Criteria::FREQUENCY_PUNCTUAL == $sameDayProposal->getCriteria()->getFrequency()) {
            $adOutwardTime = new DateTime($ad->getOutwardTime());
            $adOriginDateTime = $ad->getOutwardDate()->setTime($adOutwardTime->format('H'), $adOutwardTime->format('i'), 0);

            $arrivalDateTimeOfProposal = $sameDayProposal->getCriteria()->getArrivalDateTime();
            $arrivalHourToNextOrigin = $arrivalDateTimeOfProposal->add(new DateInterval('PT'.$travelDurationBetweenProposalDestinationAndAdOrigin.'S'));

            if ($adOriginDateTime <= $arrivalHourToNextOrigin) {
                return new AntiFraudResponse(false, AntiFraudException::NOT_ENOUGH_TIME);
            }
        }

        // punctual Ad match regular ad
        if (Criteria::FREQUENCY_PUNCTUAL == $ad->getFrequency() && Criteria::FREQUENCY_REGULAR == $sameDayProposal->getCriteria()->getFrequency()) {
            $adOutwardTime = new DateTime($ad->getOutwardTime());
            $adOriginDateTime = $ad->getOutwardDate()->setTime($adOutwardTime->format('H'), $adOutwardTime->format('i'), 0);

            $day = strtolower($adOriginDateTime->format('D'));

            // We check if there is a time for this day in the matching proposal
            switch ($day) {
                case 'sun':
                    $arrivalHourToNextOrigin = $sameDayProposal->getCriteria()->getArrivalSunTime()->add(new DateInterval('PT'.$travelDurationBetweenProposalDestinationAndAdOrigin.'S'));

                    break;

                case 'mon':
                    $arrivalHourToNextOrigin = $sameDayProposal->getCriteria()->getArrivalMonTime()->add(new DateInterval('PT'.$travelDurationBetweenProposalDestinationAndAdOrigin.'S'));

                    break;

                case 'tue':
                    $arrivalHourToNextOrigin = $sameDayProposal->getCriteria()->getArrivalTueTime()->add(new DateInterval('PT'.$travelDurationBetweenProposalDestinationAndAdOrigin.'S'));

                    break;

                case 'wed':
                    $arrivalHourToNextOrigin = $sameDayProposal->getCriteria()->getArrivalWedTime()->add(new DateInterval('PT'.$travelDurationBetweenProposalDestinationAndAdOrigin.'S'));

                    break;

                case 'thu':
                    $arrivalHourToNextOrigin = $sameDayProposal->getCriteria()->getArrivalThuTime()->add(new DateInterval('PT'.$travelDurationBetweenProposalDestinationAndAdOrigin.'S'));

                    break;

                case 'fri':
                    $arrivalHourToNextOrigin = $sameDayProposal->getCriteria()->getArrivalFriTime()->add(new DateInterval('PT'.$travelDurationBetweenProposalDestinationAndAdOrigin.'S'));

                    break;

                case 'sat':
                    $arrivalHourToNextOrigin = $sameDayProposal->getCriteria()->getArrivalSatTime()->add(new DateInterval('PT'.$travelDurationBetweenProposalDestinationAndAdOrigin.'S'));

                    break;
            }

            if (!is_null($arrivalHourToNextOrigin)) {
                if ($adOriginDateTime <= $arrivalHourToNextOrigin) {
                    return new AntiFraudResponse(false, AntiFraudException::NOT_ENOUGH_TIME);
                }
            }
        }

        // regular Ad match regular ad
        if (Criteria::FREQUENCY_REGULAR == $ad->getFrequency() && Criteria::FREQUENCY_REGULAR == $sameDayProposal->getCriteria()->getFrequency()) {
            $days = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'];

            foreach ($days as $day) {
                // We check if the Ad we are trying to post as a monday check in a schedule
                foreach ($ad->getSchedule() as $schedule) {
                    if (isset($schedule[$day]) && $schedule[$day]) {
                        $adOriginDateTime = new \DateTime('now');
                        $outwardTime = explode(':', $schedule['outwardTime']);
                        $adOriginDateTime->setTime((int) $outwardTime[0], (int) $outwardTime[1]);

                        // We check if there is a time for this day in the matching proposal
                        switch ($day) {
                            case 'sun':
                                $arrivalHourToNextOrigin = $sameDayProposal->getCriteria()->getArrivalSunTime()->add(new DateInterval('PT'.$travelDurationBetweenProposalDestinationAndAdOrigin.'S'));

                                break;

                            case 'mon':
                                $arrivalHourToNextOrigin = $sameDayProposal->getCriteria()->getArrivalMonTime()->add(new DateInterval('PT'.$travelDurationBetweenProposalDestinationAndAdOrigin.'S'));

                                break;

                            case 'tue':
                                $arrivalHourToNextOrigin = $sameDayProposal->getCriteria()->getArrivalTueTime()->add(new DateInterval('PT'.$travelDurationBetweenProposalDestinationAndAdOrigin.'S'));

                                break;

                            case 'wed':
                                $arrivalHourToNextOrigin = $sameDayProposal->getCriteria()->getArrivalWedTime()->add(new DateInterval('PT'.$travelDurationBetweenProposalDestinationAndAdOrigin.'S'));

                                break;

                            case 'thu':
                                $arrivalHourToNextOrigin = $sameDayProposal->getCriteria()->getArrivalThuTime()->add(new DateInterval('PT'.$travelDurationBetweenProposalDestinationAndAdOrigin.'S'));

                                break;

                            case 'fri':
                                $arrivalHourToNextOrigin = $sameDayProposal->getCriteria()->getArrivalFriTime()->add(new DateInterval('PT'.$travelDurationBetweenProposalDestinationAndAdOrigin.'S'));

                                break;

                            case 'sat':
                                $arrivalHourToNextOrigin = $sameDayProposal->getCriteria()->getArrivalSatTime()->add(new DateInterval('PT'.$travelDurationBetweenProposalDestinationAndAdOrigin.'S'));

                                break;
                        }

                        if (!is_null($arrivalHourToNextOrigin)) {
                            if ($adOriginDateTime <= $arrivalHourToNextOrigin) {
                                return new AntiFraudResponse(false, AntiFraudException::NOT_ENOUGH_TIME);
                            }
                        }
                    }
                }
            }
        }

        // regular Ad match punctual ad
        if (Criteria::FREQUENCY_REGULAR == $ad->getFrequency() && Criteria::FREQUENCY_PUNCTUAL == $sameDayProposal->getCriteria()->getFrequency()) {
            $day = strtolower($sameDayProposal->getCriteria()->getArrivalDateTime()->format('D'));

            // We check if the Ad we are trying to post as a monday check in a schedule
            foreach ($ad->getSchedule() as $schedule) {
                if (isset($schedule[$day]) && $schedule[$day]) {
                    $adOriginDateTime = new \DateTime('now');
                    $outwardTime = explode(':', $schedule['outwardTime']);
                    $adOriginDateTime->setTime((int) $outwardTime[0], (int) $outwardTime[1]);

                    $arrivalDateTimeOfProposal = $sameDayProposal->getCriteria()->getArrivalDateTime();
                    $arrivalHourToNextOrigin = $arrivalDateTimeOfProposal->add(new DateInterval('PT'.$travelDurationBetweenProposalDestinationAndAdOrigin.'S'));

                    if (!is_null($arrivalHourToNextOrigin)) {
                        if ($adOriginDateTime <= $arrivalHourToNextOrigin) {
                            return new AntiFraudResponse(false, AntiFraudException::NOT_ENOUGH_TIME);
                        }
                    }
                }
            }
        }

        return new AntiFraudResponse(true, AntiFraudException::OK);
    }

    /**
     * Anti Fraud System first check - Max number of journeys
     * A user can only have $nbCarpoolsMax on the same day.
     *
     * @param Ad $ad The Ad we are trying to post
     */
    private function validAdFirstCheck(Ad $ad): AntiFraudResponse
    {
        // By default, the outward date is immutable, we need to make a regular Datetime
        $dateTime = new \DateTime(null, $ad->getOutwardDate()->getTimezone());
        $dateTime->setTimestamp($ad->getOutwardDate()->getTimestamp());

        // Setup the User if it exists
        $user = null;
        if (!is_null($ad->getUser())) {
            $user = $ad->getUser();
        } elseif (!is_null($ad->getUserId())) {
            $user = $this->userManager->getUser($ad->getUserId());
        }

        $excludedProposalIds = [];
        if (!is_null($ad->getProposalId())) {
            array_push($excludedProposalIds, $ad->getProposalId());
        }
        $proposals = $this->proposalRepository->findByDate($dateTime, $user, true, $this->distanceMinCheck * 1000, null, $excludedProposalIds);

        if (!is_null($proposals) && is_array($proposals) && count($proposals) >= $this->nbCarpoolsMax) {
            $this->sameDayProposals = $proposals;

            return new AntiFraudResponse(false, AntiFraudException::TOO_MANY_AD);
        }

        return new AntiFraudResponse(true, AntiFraudException::OK);
    }

    /**
     * Check if the hours are valid regarding a proposal that is active on the same day.
     *
     * @param Ad       $ad              The Ad we are trying to post
     * @param Proposal $sameDayProposal The Proposal active on the same day that the Ad
     */
    private function checkValidHours(Ad $ad, Proposal $sameDayProposal): AntiFraudResponse
    {
        if (Criteria::FREQUENCY_PUNCTUAL == $sameDayProposal->getCriteria()->getFrequency()) {
            $dayOfTheProposal = $sameDayProposal->getCriteria()->getFromDate()->format('D');

            $arrivalProposalDateTime = $sameDayProposal->getCriteria()->getArrivalDateTime();
            if (Criteria::FREQUENCY_REGULAR == $ad->getFrequency()) {
                foreach ($ad->getSchedule() as $schedule) {
                    if (isset($schedule[strtolower($dayOfTheProposal)]) && $schedule[strtolower($dayOfTheProposal)]) {
                        $adOutwardDateTime = DateTime::createFromFormat('U', $sameDayProposal->getCriteria()->getFromDate()->getTimestamp());
                        $outwardTime = explode(':', $schedule['outwardTime']);
                        $adOutwardDateTime->setTime((int) $outwardTime[0], (int) $outwardTime[1]);

                        break;
                    }
                }
            } else {
                $adOutwardDateTime = DateTime::createFromFormat('U', $sameDayProposal->getCriteria()->getFromDate()->getTimestamp());
                $outwardTime = explode(':', $ad->getOutwardTime());
                $adOutwardDateTime->setTime((int) $outwardTime[0], (int) $outwardTime[1]);
            }

            if ($adOutwardDateTime <= $arrivalProposalDateTime) {
                return new AntiFraudResponse(false, AntiFraudException::INVALID_TIME);
            }

            return new AntiFraudResponse(true, AntiFraudException::OK);
        }
        // Regular journey

        // We will only compare the hours without considering the date

        $days = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'];

        foreach ($days as $day) {
            // We check if the Ad we are trying to post as a monday check in a schedule

            foreach ($ad->getSchedule() as $schedule) {
                if (isset($schedule[$day]) && $schedule[$day]) {
                    $adDateTime = new \DateTime('now');
                    $outwardTime = explode(':', $schedule['outwardTime']);
                    $adDateTime->setTime((int) $outwardTime[0], (int) $outwardTime[1]);

                    // We check if there is a time for this day in the matching proposal
                    switch ($day) {
                        case 'sun': $arrivalProposalTime = $sameDayProposal->getCriteria()->getArrivalSunTime();

                            break;

                        case 'mon': $arrivalProposalTime = $sameDayProposal->getCriteria()->getArrivalMonTime();

                            break;

                        case 'tue': $arrivalProposalTime = $sameDayProposal->getCriteria()->getArrivalTueTime();

                            break;

                        case 'wed': $arrivalProposalTime = $sameDayProposal->getCriteria()->getArrivalWedTime();

                            break;

                        case 'thu': $arrivalProposalTime = $sameDayProposal->getCriteria()->getArrivalThuTime();

                            break;

                        case 'fri': $arrivalProposalTime = $sameDayProposal->getCriteria()->getArrivalFriTime();

                            break;

                        case 'sat': $arrivalProposalTime = $sameDayProposal->getCriteria()->getArrivalSatTime();

                            break;
                    }

                    if (!is_null($arrivalProposalTime)) {
                        if ($adDateTime <= $arrivalProposalTime) {
                            return new AntiFraudResponse(false, AntiFraudException::INVALID_TIME);
                        }
                    }
                }
            }
        }

        return new AntiFraudResponse(true, AntiFraudException::OK);
    }

    /**
     * Check if the hours are valid regarding an unpaused ad that is active on the same day.
     *
     * @param Ad       $ad              The Ad we are trying to unpaused
     * @param Proposal $sameDayProposal The Proposal active on the same day that the Ad
     */
    private function checkValidHoursUnpausedAd(Ad $ad, Proposal $sameDayProposal): AntiFraudResponse
    {
        if (Criteria::FREQUENCY_PUNCTUAL == $sameDayProposal->getCriteria()->getFrequency()) {
            if (Criteria::FREQUENCY_PUNCTUAL == $ad->getFrequency()) {
                $outwardDate = DateTime::createFromFormat('U', $ad->getOutwardDate()->getTimestamp());
                $outwardTime = explode(':', $ad->getOutwardTime());
                $outwardDate->setTime((int) $outwardTime[0], (int) $outwardTime[1]);
                $arrivalDateTime = $sameDayProposal->getCriteria()->getArrivalDateTime();

                if ($outwardDate <= $arrivalDateTime) {
                    return new AntiFraudResponse(false, AntiFraudException::INVALID_TIME);
                }
            } else {
                $dayOfTheProposal = $sameDayProposal->getCriteria()->getFromDate()->format('D');

                $arrivalProposalDateTime = $sameDayProposal->getCriteria()->getArrivalDateTime();
                $schedule = $ad->getSchedule();

                if (isset($schedule[strtolower($dayOfTheProposal)]) && $schedule[strtolower($dayOfTheProposal)]) {
                    $adOutwardDateTime = DateTime::createFromFormat('U', $sameDayProposal->getCriteria()->getFromDate()->getTimestamp());

                    switch (strtolower($dayOfTheProposal)) {
                        case 'sun':
                            $adOutwardDateTime->setTime((new DateTime($schedule['sunOutwardTime']))->format('H'), (new DateTime($schedule['sunOutwardTime']))->format('i'));

                            break;

                        case 'mon':
                            $adOutwardDateTime->setTime((new DateTime($schedule['monOutwardTime']))->format('H'), (new DateTime($schedule['monOutwardTime']))->format('i'));

                            break;

                        case 'tue':
                            $adOutwardDateTime->setTime((new DateTime($schedule['tueOutwardTime']))->format('H'), (new DateTime($schedule['tueOutwardTime']))->format('i'));

                            break;

                        case 'wed':
                            $adOutwardDateTime->setTime((new DateTime($schedule['wedOutwardTime']))->format('H'), (new DateTime($schedule['wedOutwardTime']))->format('i'));

                            break;

                        case 'thu':
                            $adOutwardDateTime->setTime((new DateTime($schedule['thuOutwardTime']))->format('H'), (new DateTime($schedule['thuOutwardTime']))->format('i'));

                            break;

                        case 'fri':
                            $adOutwardDateTime->setTime((new DateTime($schedule['friOutwardTime']))->format('H'), (new DateTime($schedule['friOutwardTime']))->format('i'));

                            break;

                        case 'sat':
                            $adOutwardDateTime->setTime((new DateTime($schedule['satOutwardTime']))->format('H'), (new DateTime($schedule['satOutwardTime']))->format('i'));

                            break;
                    }
                }

                if ($adOutwardDateTime <= $arrivalProposalDateTime) {
                    return new AntiFraudResponse(false, AntiFraudException::INVALID_TIME);
                }

                return new AntiFraudResponse(true, AntiFraudException::OK);
            }
        } else {
            if (Criteria::FREQUENCY_PUNCTUAL == $ad->getFrequency()) {
                $dayOfTheAd = $ad->getOutwardDate()->format('D');

                switch ($dayOfTheAd) {
                    case 'Sun': $arrivalTime = $sameDayProposal->getCriteria()->getArrivalSunTime();

                        break;

                    case 'Mon': $arrivalTime = $sameDayProposal->getCriteria()->getArrivalMonTime();

                        break;

                    case 'Tue': $arrivalTime = $sameDayProposal->getCriteria()->getArrivalTueTime();

                        break;

                    case 'Wed': $arrivalTime = $sameDayProposal->getCriteria()->getArrivalWedTime();

                        break;

                    case 'Thu': $arrivalTime = $sameDayProposal->getCriteria()->getArrivalThuTime();

                        break;

                    case 'Fri': $arrivalTime = $sameDayProposal->getCriteria()->getArrivalFriTime();

                        break;

                    case 'Sat': $arrivalTime = $sameDayProposal->getCriteria()->getArrivalSatTime();

                        break;
                }

                $arrivalProposalDateTime = DateTime::createFromFormat('U', $ad->getOutwardDate()->getTimestamp());
                $arrivalProposalDateTime->setTime($arrivalTime->format('H'), $arrivalTime->format('i'));

                $adOutwardDateTime = DateTime::createFromFormat('U', $ad->getOutwardDate()->getTimestamp());
                $outwardTime = explode(':', $ad->getOutwardTime());
                $adOutwardDateTime->setTime((int) $outwardTime[0], (int) $outwardTime[1]);

                if ($adOutwardDateTime <= $arrivalProposalDateTime) {
                    return new AntiFraudResponse(false, AntiFraudException::INVALID_TIME);
                }
            } else {
                // We post a regular that matches a Regular
                // We will only compare the hours without considering the date

                $days = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'];

                foreach ($days as $day) {
                    // We check if the Ad we are trying to post as a monday check in a schedule

                    $schedule = $ad->getSchedule();

                    if (isset($schedule[$day]) && $schedule[$day]) {
                        $adDateTime = new \DateTime('now');

                        // We check if there is a time for this day in the matching proposal
                        switch ($day) {
                            case 'sun':
                                $adDateTime = $schedule['sunOutwardTime'];
                                $arrivalProposalTime = $sameDayProposal->getCriteria()->getArrivalSunTime();

                                break;

                            case 'mon':
                                $adDateTime = $schedule['monOutwardTime'];
                                $arrivalProposalTime = $sameDayProposal->getCriteria()->getArrivalMonTime();

                                break;

                            case 'tue':
                                $adDateTime = $schedule['tueOutwardTime'];
                                $arrivalProposalTime = $sameDayProposal->getCriteria()->getArrivalTueTime();

                                break;

                            case 'wed':
                                $adDateTime = $schedule['wedOutwardTime'];
                                $arrivalProposalTime = $sameDayProposal->getCriteria()->getArrivalWedTime();

                                break;

                            case 'thu':
                                $adDateTime = $schedule['thuOutwardTime'];
                                $arrivalProposalTime = $sameDayProposal->getCriteria()->getArrivalThuTime();

                                break;

                            case 'fri':
                                $adDateTime = $schedule['friOutwardTime'];
                                $arrivalProposalTime = $sameDayProposal->getCriteria()->getArrivalFriTime();

                                break;

                            case 'sat':
                                $adDateTime = $schedule['satOutwardTime'];
                                $arrivalProposalTime = $sameDayProposal->getCriteria()->getArrivalSatTime();

                                break;
                        }

                        if (!is_null($arrivalProposalTime)) {
                            if ($adDateTime <= $arrivalProposalTime) {
                                return new AntiFraudResponse(false, AntiFraudException::INVALID_TIME);
                            }
                        }
                    }
                }
            }
        }

        return new AntiFraudResponse(true, AntiFraudException::OK);
    }
}