<?php
namespace App\Service;
use App\Dto\ProfileListSpecification;
use App\Entity\Location\City;
use App\Entity\Location\County;
use App\Entity\Location\District;
use App\Entity\Location\Station;
use App\Entity\Profile\BodyTypes;
use App\Entity\Profile\BreastTypes;
use App\Entity\Profile\Genders;
use App\Entity\Profile\HairColors;
use App\Entity\Profile\Nationalities;
use App\Entity\Profile\PrivateHaircuts;
use App\Entity\Service;
use App\Entity\ServiceGroups;
use App\Entity\TakeOutLocations;
use App\Repository\DistrictRepository;
use App\Repository\ProfileRepository;
use App\Repository\ServiceRepository;
use App\Specification\ElasticSearch\ISpecification;
use App\Specification\Profile\BdsmProfile;
use App\Specification\Profile\ProfileAdBoardPlacement;
use App\Specification\Profile\ProfileHasApartments;
use App\Specification\Profile\ProfileHasComments;
use App\Specification\Profile\ProfileHasHighPrice;
use App\Specification\Profile\ProfileHasLowPrice;
use App\Specification\Profile\ProfileHasOnCall;
use App\Specification\Profile\ProfileHasOneOfGenders;
use App\Specification\Profile\ProfileHasPriceGreaterThan;
use App\Specification\Profile\ProfileHasSelfie;
use App\Specification\Profile\ProfileHasVideo;
use App\Specification\Profile\ProfileInMoskowDistricts;
use App\Specification\Profile\ProfileIsActive;
use App\Specification\Profile\ProfileIsApproved;
use App\Specification\Profile\ProfileIsElite;
use App\Specification\Profile\ProfileIsForHour;
use App\Specification\Profile\ProfileIsForNight;
use App\Specification\Profile\ProfileIsGirlfriend;
use App\Specification\Profile\ProfileIsLocated;
use App\Specification\Profile\ProfileIsMasseur;
use App\Specification\Profile\ProfileIsMostExpensive;
use App\Specification\Profile\ProfileIsNew;
use App\Specification\Profile\ProfileIsNice;
use App\Specification\Profile\ProfileIsNotExpensive;
use App\Specification\Profile\ProfileIsNotHidden;
use App\Specification\Profile\ProfileIsProvidingOneOfServices;
use App\Specification\Profile\ProfileIsProvidingTakeOut;
use App\Specification\Profile\ProfileIsRealElite;
use App\Specification\Profile\ProfileIsUpdatedWithinAYear;
use App\Specification\Profile\ProfileIsVip;
use App\Specification\Profile\ProfileIsVipIndividual;
use App\Specification\Profile\ProfileIsVipMoskow;
use App\Specification\Profile\ProfileNoRetouch;
use App\Specification\Profile\ProfileWithAge;
use App\Specification\Profile\ProfileWithApartmentsOneHourPrice;
use App\Specification\Profile\ProfileWithBodyType;
use App\Specification\Profile\ProfileWithBreastType;
use App\Specification\Profile\ProfileWithHairColor;
use App\Specification\Profile\ProfileWithHeight;
use App\Specification\Profile\ProfileWithNationality;
use App\Specification\Profile\ProfileWithPrivateHaircut;
use App\Specification\QueryModifier\LimitResult;
use App\Specification\QueryModifier\ProfileAvatar;
use App\Specification\QueryModifier\ProfileOrderedByNightPrice;
use App\Specification\QueryModifier\ProfilePlacementHiding;
use Happyr\DoctrineSpecification\Filter\Filter;
use Happyr\DoctrineSpecification\Logic\AndX;
use Happyr\DoctrineSpecification\Spec;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
class ProfileListSpecificationService
{
public function __construct(
private CountryCurrencyResolver $countryCurrencyResolver,
private ServiceRepository $serviceRepository,
private ParameterBagInterface $parameterBag,
private ProfileRepository $profileRepository,
private DistrictRepository $districtRepository,
private Features $features,
) {}
public function listForMasseur(City $city): ProfileListSpecification
{
return new ProfileListSpecification(new ProfileIsMasseur(), \App\Specification\ElasticSearch\ProfileIsLocated::withinCity($city));
}
public function listByCity(): ProfileListSpecification
{
return new ProfileListSpecification(null, null);
}
public function listByCounty(County $county): ProfileListSpecification
{
return new ProfileListSpecification(ProfileIsLocated::withinCounty($county), \App\Specification\ElasticSearch\ProfileIsLocated::withinCounty($county));
}
public function listByDistrict(District $district): ProfileListSpecification
{
return new ProfileListSpecification(ProfileIsLocated::withinDistrict($district), \App\Specification\ElasticSearch\ProfileIsLocated::withinDistrict($district));
}
public function listByStation(Station $station): ProfileListSpecification
{
return new ProfileListSpecification(ProfileIsLocated::nearStation($station), \App\Specification\ElasticSearch\ProfileIsLocated::nearStations([$station]));
}
public function listByStations(array $stations): ProfileListSpecification
{
// $stationIds = explode(',', $stations);
// $stations = $this->stationRepository->findBy(['uriIdentity' => $stationIds]);
return new ProfileListSpecification(new ProfileIsLocated(null, null, null, $stations), \App\Specification\ElasticSearch\ProfileIsLocated::nearStations($stations));
}
public function listApproved(): ProfileListSpecification
{
return new ProfileListSpecification(new ProfileIsApproved(), new \App\Specification\ElasticSearch\ProfileIsApproved());
}
public function listWithComments(): ProfileListSpecification
{
return new ProfileListSpecification(new ProfileHasComments(), new \App\Specification\ElasticSearch\ProfileHasComments());
}
public function listWithVideo(): ProfileListSpecification
{
return new ProfileListSpecification(new ProfileHasVideo(), new \App\Specification\ElasticSearch\ProfileHasVideo());
}
public function listWithSelfie(): ProfileListSpecification
{
return new ProfileListSpecification(new ProfileHasSelfie(), new \App\Specification\ElasticSearch\ProfileHasSelfie());
}
public function listByPrice(City $city, string $priceType, int $minPrice = null, int $maxPrice = null): ProfileListSpecification
{
$currency = $this->countryCurrencyResolver->getCurrencyFor($city->getCountryCode());
if ($minPrice && $maxPrice) {
$priceSpec = ProfileWithApartmentsOneHourPrice::range($minPrice, $maxPrice);
$recommendationSpec = \App\Specification\ElasticSearch\ProfileWithApartmentsOneHourPrice::range($minPrice, $maxPrice);
} elseif ($maxPrice) {
$priceSpec = ProfileWithApartmentsOneHourPrice::cheaperThan($maxPrice);
$recommendationSpec = \App\Specification\ElasticSearch\ProfileWithApartmentsOneHourPrice::cheaperThan($maxPrice);
} else {
switch ($priceType) {
case 'not_expensive':
$priceSpec = new ProfileIsNotExpensive($city);
$recommendationSpec = new \App\Specification\ElasticSearch\ProfileIsNotExpensive($currency);
break;
case 'low':
$priceSpec = new ProfileHasLowPrice($city, $currency);
$recommendationSpec = new \App\Specification\ElasticSearch\ProfileHasLowPrice($city, $currency);
break;
case 'elite':
$priceSpec = new ProfileHasHighPrice($city, $currency, [
'RUB' => 20000,
], [
'RUB' => 20000,
]);
$recommendationSpec = new \App\Specification\ElasticSearch\ProfileHasHighPrice($city, $currency, [], []);
break;
case 'high':
default:
$priceSpec = new ProfileHasHighPrice($city, $currency);
$recommendationSpec = new \App\Specification\ElasticSearch\ProfileHasHighPrice($city, $currency);
break;
}
}
return new ProfileListSpecification($priceSpec, $recommendationSpec);
}
public function listByAge(string $ageType, int $minAge = null, int $maxAge = null): ProfileListSpecification
{
if ($minAge && $maxAge) {
$ageSpec = ProfileWithAge::range($minAge, $maxAge);
$recommendationSpec = \App\Specification\ElasticSearch\ProfileWithAge::range($minAge, $maxAge);
} elseif ($minAge) {
$ageSpec = ProfileWithAge::olderThan($minAge);
$recommendationSpec = \App\Specification\ElasticSearch\ProfileWithAge::olderThan($minAge);
} else {
switch ($ageType) {
case 'old':
$ageSpec = ProfileWithAge::old();
$recommendationSpec = \App\Specification\ElasticSearch\ProfileWithAge::old();
break;
case 'young':
default:
$ageSpec = ProfileWithAge::young();
$recommendationSpec = \App\Specification\ElasticSearch\ProfileWithAge::young();
break;
}
}
return new ProfileListSpecification($ageSpec, $recommendationSpec);
}
public function listByHeight(string $heightType): ProfileListSpecification
{
if ('short' === $heightType) {
$heightSpec = ProfileWithHeight::short();
$recommendationSpec = \App\Specification\ElasticSearch\ProfileWithHeight::short();
} else {
$heightSpec = ProfileWithHeight::tall();
$recommendationSpec = \App\Specification\ElasticSearch\ProfileWithHeight::tall();
}
return new ProfileListSpecification($heightSpec, $recommendationSpec);
}
public function listByBreastType(string $breastType): ProfileListSpecification
{
$breastTypeSpec = new ProfileWithBreastType(BreastTypes::getValueByUriIdentity($breastType));
return new ProfileListSpecification($breastTypeSpec, new \App\Specification\ElasticSearch\ProfileWithBreastType(BreastTypes::getValueByUriIdentity($breastType)));
}
public function listByHairColor(string $hairColor): ProfileListSpecification
{
$hairColorSpec = new ProfileWithHairColor(HairColors::getValueByUriIdentity($hairColor));
return new ProfileListSpecification($hairColorSpec, new \App\Specification\ElasticSearch\ProfileWithHairColor(HairColors::getValueByUriIdentity($hairColor)));
}
public function listByBodyType(string $bodyType): ProfileListSpecification
{
$bodyTypeSpec = new ProfileWithBodyType(BodyTypes::getValueByUriIdentity($bodyType));
return new ProfileListSpecification($bodyTypeSpec, new \App\Specification\ElasticSearch\ProfileWithBodyType(BodyTypes::getValueByUriIdentity($bodyType)));
}
public function listByPlace(string $placeType, string $takeOutLocation = null): ?ProfileListSpecification
{
switch ($placeType) {
case 'apartments':
$placeSpec = new ProfileHasApartments();
$recommendationSpec = new \App\Specification\ElasticSearch\ProfileHasApartments();
break;
case 'take-out':
$takeOutLocation = TakeOutLocations::getValueByUriIdentity($takeOutLocation);
$placeSpec = new ProfileIsProvidingTakeOut($takeOutLocation);
$recommendationSpec = new \App\Specification\ElasticSearch\ProfileIsProvidingTakeOut($takeOutLocation);
break;
case 'on-call':
$placeSpec = Spec::andX(
new ProfileIsProvidingTakeOut(null),
Spec::not(new ProfileHasApartments())
);
$recommendationSpec = \App\Specification\ElasticSearch\Spec::andX(
(new \App\Specification\ElasticSearch\ProfileIsProvidingTakeOut(null))->getSpec(),
\App\Specification\ElasticSearch\Spec::not((new \App\Specification\ElasticSearch\ProfileHasApartments())->getSpec())
);
break;
default:
return null;
}
return new ProfileListSpecification($placeSpec, $recommendationSpec);
}
public function listByPrivateHaircut(string $privateHaircut): ProfileListSpecification
{
$privateHaircutSpec = new ProfileWithPrivateHaircut(PrivateHaircuts::getValueByUriIdentity($privateHaircut));
return new ProfileListSpecification($privateHaircutSpec, new \App\Specification\ElasticSearch\ProfileWithPrivateHaircut(PrivateHaircuts::getValueByUriIdentity($privateHaircut)));
}
public function listByNationality(string $nationality): ProfileListSpecification
{
$nationalitySpec = new ProfileWithNationality(Nationalities::getValueByUriIdentity($nationality));
return new ProfileListSpecification($nationalitySpec, new \App\Specification\ElasticSearch\ProfileWithNationality(Nationalities::getValueByUriIdentity($nationality)));
}
public function listByProvidedService(Service $service, City $city): ProfileListSpecification
{
$sameGroupServices = $this->serviceRepository->findBy(['group' => $service->getGroup()]);
$spec = new AndX(new ProfileIsProvidingOneOfServices([$service]));
if('eskort' == $service->getUriIdentity() && true === $this->features->escort_additional_requirements()) {
$spec->andX(new ProfileIsApproved());
$spec->andX(new ProfileHasPriceGreaterThan($city, 'RUB', [
'RUB' => 12000,
]));
$spec->andX(new ProfileIsUpdatedWithinAYear());
}
return new ProfileListSpecification($spec, new \App\Specification\ElasticSearch\ProfileIsProvidingOneOfServices($sameGroupServices));
}
public function listNew(int $weeks = 2): ProfileListSpecification
{
return new ProfileListSpecification(new ProfileIsNew($weeks), new \App\Specification\ElasticSearch\ProfileIsNew($weeks));
}
public function listByNoRetouch(): ProfileListSpecification
{
return new ProfileListSpecification(new ProfileNoRetouch(), new \App\Specification\ElasticSearch\ProfileNoRetouch());
}
public function listByNice(): ProfileListSpecification
{
return new ProfileListSpecification(new ProfileIsNice(), new \App\Specification\ElasticSearch\ProfileIsNice());
}
public function listByOnCall(): ProfileListSpecification
{
return new ProfileListSpecification(new ProfileHasOnCall(), new \App\Specification\ElasticSearch\ProfileHasOnCall());
}
public function listForHour(): ProfileListSpecification
{
return new ProfileListSpecification(new ProfileIsForHour(), new \App\Specification\ElasticSearch\ProfileIsForHour());
}
public function listForNight(): ProfileListSpecification
{
return new ProfileListSpecification(new ProfileIsForNight(), new \App\Specification\ElasticSearch\ProfileIsForNight());
}
private function getSpecForEliteGirls(City $city):Filter {
$minPrice = $this->countryCurrencyResolver->getValueByCountryCode($city->getCountryCode(), [
'RUB' => 5000,
'UAH' => 1500,
'USD' => 100,
'EUR' => 130,
]);
return new ProfileIsElite($minPrice);
}
private function getElasticSearchSpecForEliteGirls(City $city): ISpecification {
$minPrice = $this->countryCurrencyResolver->getValueByCountryCode($city->getCountryCode(), [
'RUB' => 5000,
'UAH' => 1500,
'USD' => 100,
'EUR' => 130,
]);
return new \App\Specification\ElasticSearch\ProfileIsElite($minPrice);
}
public function listForEliteGirls(City $city): ProfileListSpecification
{
$eliteSpec = $this->getSpecForEliteGirls($city);
return new ProfileListSpecification($eliteSpec, $this->getElasticSearchSpecForEliteGirls($city));
}
public function listForRealElite(City $city): ProfileListSpecification
{
$minPrice = $this->countryCurrencyResolver->getValueByCountryCode($city->getCountryCode(), [
'RUB' => 5000,
'UAH' => 1500,
'USD' => 100,
'EUR' => 130,
]);
return new ProfileListSpecification(new ProfileIsRealElite($minPrice), new \App\Specification\ElasticSearch\ProfileIsRealElite($minPrice));
}
public function listForVipPros(City $city): ProfileListSpecification
{
$minPrice = $this->countryCurrencyResolver->getValueByCountryCode($city->getCountryCode(), [
'RUB' => 4000,
'UAH' => 1200,
'USD' => 100,
'EUR' => 100,
]);
$districts = [];
foreach (ProfileInMoskowDistricts::$districtsUri as $uri) {
$districts[] = $this->districtRepository->ofUriIdentityWithinCity($uri, $city);
}
return new ProfileListSpecification(new ProfileIsVip($city, $districts, $minPrice), new \App\Specification\ElasticSearch\ProfileIsVip($city, $districts, $minPrice));
}
public function listForVipIndividual(City $city): ProfileListSpecification
{
$minPrice = $this->countryCurrencyResolver->getValueByCountryCode($city->getCountryCode(), [
'RUB' => 30000,
'UAH' => 10000,
'USD' => 400,
'EUR' => 400,
]);
return new ProfileListSpecification(new ProfileIsVipIndividual($minPrice), new \App\Specification\ElasticSearch\ProfileIsVipIndividual($minPrice));
}
public function listForVipGirlsCity(City $city): ProfileListSpecification
{
return new ProfileListSpecification(new ProfileIsVipMoskow($city), new \App\Specification\ElasticSearch\ProfileIsVipMoskow($city));
}
public function listOfGirlfriends(): ProfileListSpecification
{
return new ProfileListSpecification(new ProfileIsGirlfriend(), new \App\Specification\ElasticSearch\ProfileIsGirlfriend());
}
public function listOfMostExpensive(City $city): ProfileListSpecification
{
$count = intval($this->profileRepository->match(Spec::countOf(Spec::andX(
ProfileIsLocated::withinCity($city),
new ProfileHasOneOfGenders([Genders::FEMALE])
)))[0][1]);
$criteria = Spec::andX(
$this->features->free_profiles() ? new ProfileIsNotHidden() : new ProfileIsActive(),
new ProfileAdBoardPlacement(),
new ProfilePlacementHiding(),
new ProfileAvatar(),
ProfileIsLocated::withinCity($city),
new LimitResult(round($count / 10), 0),
new ProfileOrderedByNightPrice(),
new ProfileHasOneOfGenders([Genders::FEMALE])
);
$profilesTenPercents = $this->profileRepository->match($criteria);
$criteriaProfile = end($profilesTenPercents);
return new ProfileListSpecification(new ProfileIsMostExpensive($criteriaProfile), new \App\Specification\ElasticSearch\ProfileIsMostExpensive($criteriaProfile));
}
public function listBdsm(): ProfileListSpecification
{
$bdsmIds = $this->serviceRepository->findBy(['group' => ServiceGroups::BDSM]);
return new ProfileListSpecification(null, new \App\Specification\ElasticSearch\ProfileIsProvidingOneOfServices($bdsmIds), [new BdsmProfile($this->parameterBag->get('profile_list.bdsm_page.min_profile_services'))]);
}
public function listByGender(string $gender): ProfileListSpecification
{
$gender = Genders::getValueByUriIdentity($gender);
$genders = [$gender];
return new ProfileListSpecification(null, null, null, $genders);
}
}