<?php 
 
namespace Slivki\Controller; 
 
use Doctrine\ORM\Query; 
use Slivki\Dao\Comment\CommentRatingDaoInterface; 
use Slivki\Dao\GiftCertificate\GiftCertificateDaoInterface; 
use Slivki\Dao\Offer\FavouriteOfferDaoInterface; 
use Slivki\Dao\Offer\OfferDaoInterface; 
use Slivki\Dao\OfferCode\PurchaseCountWithCorrectionDaoInterface; 
use Slivki\Dao\TireFilter\TireFilterDaoInterface; 
use Slivki\Entity\AlternativeOffer; 
use Slivki\Entity\BankCurrency; 
use Slivki\Entity\Banner\MobileLandingBanner; 
use Slivki\Entity\Director; 
use Slivki\Entity\FoodOrder; 
use Slivki\Entity\GiftCertificate; 
use Slivki\Entity\MailingCampaign; 
use Slivki\Entity\SearchQuery; 
use Slivki\Entity\Seo; 
use Slivki\Entity\UserPhone; 
use Slivki\Enum\SwitcherFeatures; 
use Slivki\Repository\Comment\CommentRepositoryInterface; 
use Slivki\Services\CategoryBoxCacheService; 
use Slivki\Services\City\CityProvider; 
use Slivki\Services\Comment\CommentCacheService; 
use Slivki\Services\Comment\CommentService; 
use Slivki\Services\DeviceTypeService; 
use Slivki\Services\Offer\GalleryVideo\VideoPackageDtoGetter; 
use Slivki\Services\Sidebar\SidebarCacheService; 
use Slivki\Services\Subscription\SubscriptionService; 
use Slivki\Services\Switcher\ServerFeatureStateChecker; 
use Slivki\Services\VoiceMessage\VoiceMessageUploader; 
use Slivki\Util\Iiko\Dodo; 
use Slivki\Util\Iiko\Dominos; 
use Symfony\Component\HttpFoundation\Cookie; 
use Symfony\Component\Routing\Annotation\Route; 
use Slivki\Entity\Booking; 
use Slivki\Entity\Category; 
use Slivki\Entity\ChatSession; 
use Slivki\Entity\City; 
use Slivki\Entity\Comment; 
use Slivki\Entity\CommentLike; 
use Slivki\Entity\EntityOption; 
use Slivki\Entity\GeoLocation; 
use Slivki\Entity\HotFeed; 
use Slivki\Entity\InfoPage; 
use Slivki\Entity\MainMenu; 
use Slivki\Entity\Media; 
use Slivki\Entity\MediaType; 
use Slivki\Entity\Offer; 
use Slivki\Entity\OfferOrder; 
use Slivki\Entity\OfferProposal; 
use Slivki\Entity\ReadabilityStat; 
use Slivki\Entity\Sale; 
use Slivki\Entity\SearchStatistic; 
use Slivki\Entity\Subscriber; 
use Slivki\Entity\User; 
use Slivki\Entity\UserGroup; 
use Slivki\Repository\CityRepository; 
use Slivki\Repository\CommentRepository; 
use Slivki\Repository\OfferRepository; 
use Slivki\Repository\SaleRepository; 
use Slivki\Repository\SeoRepository; 
use Slivki\Services\BannerService; 
use Slivki\Services\CacheService; 
use Slivki\Services\Category\CategoryCacheService; 
use Slivki\Services\Mailer; 
use Slivki\Services\Offer\OfferCacheService; 
use Slivki\Services\Sale\SaleCacheService; 
use Slivki\Services\TextCacheService; 
use Slivki\Twig\SlivkiTwigExtension; 
use Slivki\Util\CommonUtil; 
use Slivki\Util\Iiko\AbstractDelivery; 
use Slivki\Util\Iiko\SushiHouse; 
use Slivki\Services\ImageService; 
use Slivki\Util\Logger; 
use Slivki\Util\SoftCache; 
use Slivki\Util\CensureLibrary\Censure; 
use Symfony\Component\HttpFoundation\JsonResponse; 
use Symfony\Component\HttpFoundation\Request; 
use Symfony\Component\HttpFoundation\Response; 
use Symfony\Component\Filesystem\Filesystem; 
use Slivki\Entity\Visit; 
use Symfony\Component\HttpKernel\KernelInterface; 
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; 
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; 
use Symfony\Component\Validator\Constraints\Email; 
 
class DefaultController extends SiteController { 
    const OFFERS_PER_PAGE = 24; 
    const LONG_CATEGORIES_PER_PAGE = 36; 
    const SIDEBAR_CACHE_PATH = '/slivki-cache/sidebar'; 
    const CATEGORY_BOX_PATH = '/slivki-cache/categorybox/'; 
    const LONG_CATEGORIES = [ 
        Category::TIRE_FITTING_CATEGORY_ID, 
        Category::LASER_EPILATION_CATEGORY_ID, 
    ]; 
 
    private TireFilterDaoInterface $tireFilterDao; 
    private OfferDaoInterface $offerDao; 
    private CommentRatingDaoInterface $commentRatingDao; 
 
 
    public function __construct( 
        KernelInterface           $kernel, 
        TireFilterDaoInterface    $tireFilterDao, 
        OfferDaoInterface         $offerDao, 
        CommentRatingDaoInterface $commentRatingDao 
    ) { 
        parent::__construct($kernel); 
        $this->tireFilterDao = $tireFilterDao; 
        $this->offerDao = $offerDao; 
        $this->commentRatingDao = $commentRatingDao; 
    } 
 
    /** 
     * @Route("/", name="homepage") 
     */ 
    public function indexAction( 
        Request $request, 
        BannerService $bannerService, 
        OfferCacheService $offerCacheService, 
        SaleCacheService $saleCacheService, 
        CategoryBoxCacheService $categoryBoxCacheService, 
        CategoryCacheService $categoryCacheService, 
        CommentRepositoryInterface $commentRepository 
    ) { 
        $isMobileDevice = self::getMobileDevice($request); 
        if ($request->getHost() == 'm.slivki.by') { 
            return $this->render($isMobileDevice? 'Slivki/m/mobile/index.html.twig' : 'Slivki/m/index.html.twig'); 
        } 
        $cityID = $request->getSession()->get(City::CITY_ID_SESSION_KEY, City::DEFAULT_CITY_ID); 
        $referrer = $request->headers->get('referer', ''); 
        $response = new Response(); 
        if (self::getMobileDevice($request)) { 
            $urlParsed = parse_url($referrer); 
            $referrerHost = isset($urlParsed['host']) ? $urlParsed['host']: ''; 
            if ($referrer != $request->getSchemeAndHttpHost() . '/landing' && $referrerHost != $request->getHost() && $cityID == City::DEFAULT_CITY_ID) { 
                return $this->redirect('/landing'); 
            } 
        } 
        $mainMenuItems = $this->getDoctrine()->getManager()->getRepository(MainMenu::class)->getItemListCached(MainMenu::MENU_ID_MAIN, $cityID); 
        $categoryBoxList = []; 
        $expanded = count($mainMenuItems) == 1; 
        $i = 0; 
        foreach ($mainMenuItems as $menuItem) { 
            $category = $categoryCacheService->getCategory($menuItem->getEntityID()); 
 
            $categoryBoxTarantool = $categoryBoxCacheService->getCategoryBox( 
                $menuItem->getEntityID(), 
                CategoryBoxCacheService::SORT_BY_DEFAULT_COLUMN, 
                $isMobileDevice, 
                0, 
                $category[8] 
            ); 
 
            $categoryBox = \str_replace( 
                '<!-- categoryTeasersPlaceholder -->', 
                $categoryBoxTarantool['categoryBoxHtml'], 
                $categoryBoxCacheService->getCategoryPage($menuItem->getEntityID(), false, $isMobileDevice) 
            ); 
 
            if ($categoryBox) { 
                $categoryBoxList[$menuItem->getEntityID()] = $categoryBox; 
            } 
            if ($i == 0) { 
                break; 
            } 
            $i++; 
        } 
 
        // getSidebarBanner 
        if (!$isMobileDevice) { 
            if ($this->getUser() && $this->getUser()->hasRole(UserGroup::ROLE_ADS_FREE)) { 
                $data['sidebarBanner'] = ''; 
            } else { 
                $sidebarBannerCached = $bannerService->getSidebarBannerCached($cityID); 
                $data['sidebarBanner'] = $sidebarBannerCached['html']; 
            } 
 
            $data['lastComments'] = $commentRepository->getLastOfferComments(3); 
        } 
 
        $data['categoryBoxList'] = $categoryBoxList; 
        $data['salesList'] = $this->getSaleRepository()->getMainSales($cityID, self::getMobileDevice($request)); 
        $data['mainPage'] = true; 
        $data['smallCity'] = $expanded; 
        $deviceType = $isMobileDevice ? self::DEVICE_TYPE_MOBILE : self::DEVICE_TYPE_DESKTOP; 
        $this->addVisit(0, Visit::TYPE_MAIN_PAGE, $deviceType, $this->getUser(), $request->getClientIp(), $referrer); 
        $data['cityID'] = $cityID; 
        $request->attributes->set('mainPage', true); 
        $data['offerRateSchedule'] = $this->getOfferRateSchedule($request->getSession()); 
        $mainHotFeed = ''; 
        $mainHotFeedEntities = $this->getMainHotFeed($offerCacheService, $saleCacheService, 20, 0, HotFeed::TYPE_MAIN_PAGE, $cityID); 
        if (count($mainHotFeedEntities) > 4) { 
            foreach ($mainHotFeedEntities as $entity) { 
                $mainHotFeed .= $this->renderView($isMobileDevice ? 'Slivki/mobile/hot_feed_teaser.html.twig' 
                    : 'Slivki/top_main_feed_teaser_mobile.html.twig',  $entity); 
            } 
        } 
        $data['mainHotFeed'] = $mainHotFeed; 
        $topLevelCategoryIDList = []; 
        foreach ($mainMenuItems as $menuItem) { 
            $topLevelCategoryIDList[] = $menuItem->getEntityID(); 
        } 
        $data['topLevelCategoryIDList'] = $topLevelCategoryIDList; 
        $response->setContent($this->renderView($isMobileDevice ? 'Slivki/mobile/index.html.twig' : 'Slivki/index.html.twig', $data)); 
 
        return $response; 
    } 
 
    /** @Route("/auth") */ 
    public function auth(Request $request) { 
        $path = $request->query->get('path', '/'); 
        $response = new Response(); 
        if ($this->getUser()) { 
            return $this->redirect($path); 
        } 
        return $this->render(CommonUtil::isMobileDevice($request) ? 'Slivki/mobile/auth.html.twig' : 'Slivki/auth.html.twig'); 
    } 
 
    /** @Route("/main_top_sales/{offset}") */ 
    public function getMainOffsetSales(Request $request, $offset) { 
        $cityID = $request->getSession()->get(City::CITY_ID_SESSION_KEY, City::DEFAULT_CITY_ID); 
        $result = []; 
        $data['sale'] = $this->getSaleRepository()->getMainSales($cityID, self::getMobileDevice($request), $offset); 
        foreach ($data['sale'] as $sale) { 
            $result['result'][] = $this->renderView('Slivki/news/mobile_top_news.html.twig', $sale); 
        } 
        return new JsonResponse($result); 
    } 
 
    /** @Route("/main_hot_feed/{limit}/{offset}/{type}/{isNewMobileVersion}/{cityID}") */ 
    public function getMainHotFeedAction(Request $request, SaleCacheService $saleCacheService, OfferCacheService $offerCacheService, $limit, $offset, $type, $isNewMobileVersion, $cityID) { 
        $result = ''; 
        foreach ($this->getMainHotFeed($offerCacheService, $saleCacheService, $limit, $offset, $type, $cityID) as $entity) { 
            $result .= $this->renderView($isNewMobileVersion == 'true' ?  'Slivki/mobile/hot_feed_teaser.html.twig': 
                'Slivki/top_main_feed_teaser_mobile.html.twig',  $entity); 
        } 
        return new Response($result); 
    } 
 
 
    public function categoryAction( 
        Request $request, 
        CategoryCacheService $categoryCacheService, 
        CategoryBoxCacheService $categoryBoxCacheService, 
        $entityID 
    ) { 
        return $this->tarantoolCategory($request, $categoryCacheService, $categoryBoxCacheService, $entityID); 
    } 
 
    private function tarantoolCategory( 
        Request $request, 
        CategoryCacheService $categoryCacheService, 
        CategoryBoxCacheService $categoryBoxCacheService, 
        $entityID 
    ) { 
        $currentPage = $request->query->get('page', 1); 
        $isMobileDevice = CommonUtil::isMobileDevice($request); 
        $deviceType = $isMobileDevice ? self::DEVICE_TYPE_MOBILE : self::DEVICE_TYPE_DESKTOP; 
 
        $this->addVisit( 
            $entityID, 
            Visit::TYPE_OFFER_CATEGORY, 
            $deviceType, 
            $this->getUser(), 
            $request->getClientIp(), 
            $request->headers->get('referer', '') 
        ); 
 
        $category = $categoryCacheService->getCategory($entityID); 
        $response = new Response(); 
 
        $perPage = in_array($entityID, self::LONG_CATEGORIES) ? self::LONG_CATEGORIES_PER_PAGE : self::OFFERS_PER_PAGE; 
 
        $categoryBox = $categoryBoxCacheService->getCategoryBox( 
            $entityID, 
            CategoryBoxCacheService::SORT_BY_DEFAULT_COLUMN, 
            $isMobileDevice, 
            ($currentPage - 1) * $perPage, 
            $perPage, 
        ); 
 
        $categoryPageContent = str_replace( 
            '<!-- categoryTeasersPlaceholder -->', 
            $categoryBox['categoryBoxHtml'], 
            $categoryBoxCacheService->getCategoryPage($entityID, true, $isMobileDevice) 
        ); 
 
        $paginationHtml = $this->renderView('Slivki/pagination.html.twig', [ 
            'paginationID' => 'categoryPagination', 
            'total' => ceil($categoryBox['totalTeaserCount'] / self::OFFERS_PER_PAGE), 
            'current' => $currentPage, 
            'url' => $request->getRequestUri() . '?page=' 
        ]); 
 
        $categoryPageContent = str_replace( 
            '<!-- categoryPaginationPlaceholder -->', 
            $paginationHtml, 
            $categoryPageContent 
        ); 
 
        $tireFilterCategories = $this->tireFilterDao->getCategoriesByParentCategory($entityID); 
        if (0 !== \count($tireFilterCategories)) { 
            $categoryPageContent = \str_replace( 
                '<!-- tireFilerPlaceholder -->', 
                $this->renderView('Slivki/tire/tire_filter.html.twig', [ 
                    'categories' => $tireFilterCategories, 
                ]), 
                $categoryPageContent, 
            ); 
        } 
 
        $data = [ 
            'categoryID' => $entityID, 
            'category' => $categoryPageContent, 
            'brandingBannerCategoryIDs' => [$entityID] 
        ]; 
 
        $response->setContent($this->renderView($isMobileDevice ? 'Slivki/mobile/offer/category.html.twig' 
            : 'Slivki/offers/category_page.html.twig', $data)); 
 
        return $response; 
    } 
 
    public function categoryRatingAction(Request $request, TextCacheService $textCacheService, $entityID) { 
        $isMobile = CommonUtil::isMobileDevice($request); 
        $content = $textCacheService->getText($entityID, $isMobile ? TextCacheService::CATEGORY_COMPANIES_RATING_PAGE_MOBILE_TYPE : TextCacheService::CATEGORY_COMPANIES_RATING_PAGE_TYPE); 
        if (!$content) { 
            throw $this->createNotFoundException(); 
        } 
        return $this->render($isMobile ? 'Slivki/mobile/offer/category_rating_page.html.twig' : 'Slivki/offers/category_rating_page.html.twig', [ 
            'content' => $content, 
            'robotsMeta' => 'noindex, follow' 
        ]); 
    } 
 
    /** @Route("/category/load-more/{categoryID}/{pageNumber}/{sortBy}") */ 
    public function categoryLoadMoreAction( 
        Request $request, 
        CategoryBoxCacheService $categoryBoxCacheService, 
        $categoryID, 
        $pageNumber, 
        $sortBy 
    ) { 
        return $this->categoryLoadMoreTarantool($request, $categoryBoxCacheService, $categoryID, $pageNumber, $sortBy); 
    } 
 
    private function categoryLoadMoreTarantool( 
        Request $request, 
        CategoryBoxCacheService $categoryBoxCacheService, 
        $categoryID, 
        $pageNumber, 
        $sortBy 
    ) { 
        $referer = $request->headers->get('referer'); 
        if (!$referer) { 
            return new Response('Referer is invalid or empty.'); 
        } 
        $refererPathInfo = Request::create($referer)->getPathInfo(); 
 
        $isMobileDevice = CommonUtil::isMobileDevice($request); 
 
        switch ($sortBy) { 
            case 'popularity': 
                $sortByColumn = CategoryBoxCacheService::SORT_BY_PURCHASE_COLUMN; 
                break; 
            case 'visit': 
                $sortByColumn = CategoryBoxCacheService::SORT_BY_VISIT_COLUMN; 
                break; 
            case 'distance': 
            case 'timetable': 
            case 'rating': 
                break; 
            case 'update-date': 
                $sortByColumn = CategoryBoxCacheService::SORT_BY_CREATED_ON_COLUMN; 
                break; 
            case 'price-asc': 
                $sortByColumn = CategoryBoxCacheService::SORT_BY_PRICE_ASC_COLUMN; 
                break; 
            case 'price-desc': 
                $sortByColumn = CategoryBoxCacheService::SORT_BY_PRICE_DESC_COLUMN; 
                break; 
            default: 
                $sortByColumn = CategoryBoxCacheService::SORT_BY_DEFAULT_COLUMN; 
        } 
 
        $perPage = in_array($categoryID, self::LONG_CATEGORIES) ? self::LONG_CATEGORIES_PER_PAGE : self::OFFERS_PER_PAGE; 
 
        if ($sortBy == 'distance') { 
            $categoryBox = $categoryBoxCacheService->getCategoryBoxSortedByLocation( 
                $categoryID, 
                $request->cookies->get(User::CURRENT_LOCATION_COOKIE, GeoLocation::DEFAULT_LOCATION), 
                $isMobileDevice, 
                ($pageNumber - 1) * $perPage, 
                $perPage, 
            ); 
        } elseif ($sortBy == 'timetable') { 
            $categoryBox = $categoryBoxCacheService->getCategoryBoxSortedByTimetable( 
                $categoryID, 
                $isMobileDevice, 
                ($pageNumber - 1) * $perPage, 
                $perPage, 
            ); 
        } elseif ($sortBy == 'rating') { 
            $categoryBox = $categoryBoxCacheService->getCategoryBoxSortedByRating( 
                $categoryID, 
                $isMobileDevice, 
                ($pageNumber - 1) * $perPage, 
                $perPage, 
            ); 
        } else { 
            $categoryBox = $categoryBoxCacheService->getCategoryBox( 
                $categoryID, 
                $sortByColumn, 
                $isMobileDevice, 
                ($pageNumber - 1) * $perPage, 
                $perPage, 
            ); 
        } 
 
        return new JsonResponse([ 
            'teasers' => $categoryBox['categoryBoxHtml'], 
            'pagination' => $this->renderView('Slivki/pagination.html.twig', [ 
                'paginationID' => 'categoryPagination', 
                'total' => ceil($categoryBox['totalTeaserCount'] / self::OFFERS_PER_PAGE), 
                'current' => $pageNumber, 
                'url' => $refererPathInfo . '?page=' 
            ]) 
        ]); 
    } 
 
    /** 
     * @Route("/categoryBoxNotExpanded/{categoryID}") 
     */ 
    public function categoryBoxNotExpandedAction( 
        Request $request, 
        CategoryBoxCacheService $categoryBoxCacheService, 
        CategoryCacheService $categoryCacheService, 
        $categoryID 
    ) { 
        $isMobileDevice = CommonUtil::isMobileDevice($request); 
 
        $category = $categoryCacheService->getCategory($categoryID); 
 
        $categoryBoxTarantool = $categoryBoxCacheService->getCategoryBox( 
            $categoryID, 
            CategoryBoxCacheService::SORT_BY_DEFAULT_COLUMN, 
            $isMobileDevice, 
            0, 
            $category[8] 
        ); 
 
        $categoryBox = \str_replace( 
            '<!-- categoryTeasersPlaceholder -->', 
            $categoryBoxTarantool['categoryBoxHtml'], 
            $categoryBoxCacheService->getCategoryPage($categoryID, false, $isMobileDevice) 
        ); 
 
        return new Response($categoryBox); 
    } 
 
    /** 
     * @Route("/moreOffers") 
     */ 
    public function moreOffers(Request $request) { 
        $categoryID = (int)$request->query->get("categoryID", 0); 
        $data['categoryBoxID'] = $request->query->get("categoryBoxId", 0); 
        $moreCount = (int)$request->query->get("moreCount", 150); 
        $position = (int)$request->query->get("position", 0); 
        $category = $this->getCategoryRepository()->findCached($categoryID)['category']; 
        if (!$category) { 
            return new Response(); 
        } 
        $offerIDList = []; 
        $sql = "select entity_id from offer_category_position where category_id = $categoryID order by position offset $position limit $moreCount + 1"; 
        $offersByPosition = $this->getDoctrine()->getManager()->getConnection()->executeQuery($sql)->fetchAll(Query::HYDRATE_ARRAY); 
        $i = 0; 
        foreach ($offersByPosition as $offerPosition) { 
            if ($i>=$moreCount) { 
                break; 
            } 
            $offerIDList[] = $offerPosition['entity_id']; 
            $i++; 
        } 
        $softCache = new SoftCache(OfferRepository::CACHE_NAME); 
        $offerListCached = $softCache->getMulti($offerIDList); 
        if(!$offerListCached) { 
            return new Response(''); 
        } 
        $moreOfferList = []; 
        foreach ($offerListCached as $offer) { 
            $moreOfferList[] = $offer; 
        } 
        $data['position'] = $position + $moreCount; 
        $data['expanded'] = !count($offersByPosition) > count($offerIDList); 
        $data['showCollapse'] = true; 
        $data['isMailing'] = false; 
        $teaserBanners = $this->getOfferRepository()->getTeaserBanners($category); 
        $data['teaserBanners'] = $teaserBanners['teaserBanners']; 
        $data['absolutePosition'] = $position + 1; 
        $data['withVerticalBannersRowList'] = $teaserBanners['withVerticalBannersRowList']; 
        $data['category'] = $category; 
        $data['offerList'] = $moreOfferList; 
        $response = $this->render("Slivki/offers/teasers.html.twig", $data); 
        return $response; 
    } 
 
    public function detailsAction( 
        Request $request, 
        BannerService $bannerService, 
        OfferCacheService $offerCacheService, 
        CacheService $cacheService, 
        $entityID, 
        ImageService $imageService, 
        SubscriptionService $subscriptionService, 
        GiftCertificateDaoInterface $giftCertificateDao, 
        ServerFeatureStateChecker $serverFeatureStateChecker, 
        VideoPackageDtoGetter $videoPackageDtoGetter, 
        CityProvider $cityProvider, 
        PurchaseCountWithCorrectionDaoInterface $purchaseCountWithCorrectionDao, 
        FavouriteOfferDaoInterface $favouriteOfferDao, 
        CommentService $commentService 
    ) { 
        ini_set('memory_limit', '10g'); 
        $data['currentPage'] = $request->query->getInt('page', 1); 
        $this->get('session')->set('userCommentImages', []); 
        $entityManager = $this->getDoctrine()->getManager(); 
        $offerRepository = $this->getOfferRepository(); 
 
        $offer = $offerCacheService->getOffer($entityID, false, true); 
        $data['usersWithOfferInFavouritesCount'] = $favouriteOfferDao->getUsersWithOfferInFavouritesCount($entityID); 
 
        if ($offer instanceof Offer && $offer->getDefaultCityId() === City::TASHKENT_CITY_ID && $cityProvider->getDefaultCityId() !== City::TASHKENT_CITY_ID) { 
            return $this->redirect('https://www.slivki.uz' . $request->getRequestUri(), Response::HTTP_MOVED_PERMANENTLY); 
        } 
 
        if ($offer instanceof Offer && $offer->getDefaultCityId() !== City::TASHKENT_CITY_ID && $cityProvider->getDefaultCityId() === City::TASHKENT_CITY_ID) { 
            Logger::instance('excdebug')->info($offer->getDefaultCityId()); 
            Logger::instance('excdebug')->info($cityProvider->getDefaultCityId()); 
            //throw $this->createNotFoundException(); 
        } 
 
        $dreamLangPartner = $request->getSession()->get(EntityOption::OPTION_DREAMLAND_PARTNER); 
        if ($this->getUser() && $dreamLangPartner && !$entityManager->getRepository(User::class)->getDreamlandOption($this->getUser()->getID())) { 
            $option = new EntityOption(); 
            $option->setEntityTypeID(EntityOption::USER_TYPE); 
            $option->setEntityID($this->getUser()->getID()); 
            $option->setName(EntityOption::OPTION_DREAMLAND_PARTNER); 
            $option->setValue($dreamLangPartner); 
            $entityManager->persist($option); 
            $entityManager->flush(); 
            $request->getSession()->remove(EntityOption::OPTION_DREAMLAND_PARTNER); 
        } 
        $pastOffer = false; 
        $timeNow = new \DateTime(); 
        $preview = $request->query->get("preview", false); 
        if (!$offer || $preview) { 
            $offer = $offerRepository->find($entityID); 
            if (!$offer || ((!$offer->isInVisiblePeriod() || !$offer->isActive()) && !$preview)) { 
                throw $this->createNotFoundException(); 
            } 
            if ((!$offer->isActive() || $offer->getActiveTill() < $timeNow || $offer->getActiveSince() > $timeNow) && !$preview) { 
                $pastOffer = true; 
            } 
            if ($preview || $pastOffer) { 
                $mediaRepository = $this->getMediaRepository(); 
                $offer->setDetailMeidas($mediaRepository->getOfferDetailMedias($offer->getID())); 
                $offer->setShopMedias($mediaRepository->getOfferShopMedias($offer->getID())); 
            } else { 
                Logger::instance('CACHE DEBUG')->info('Offer ' . $entityID . ' is not found in cache'); 
                $offer = $offerCacheService->reloadOfferCache($entityID); 
            } 
        } 
        if ((!$offer->isActive() || $offer->getActiveTill() < $timeNow || $offer->getActiveSince() > $timeNow) && !$preview) { 
            $pastOffer = true; 
        } 
        if ($entityID == 131841 && ($pastOffer || $offer->isHidden())) { // issue #2688 
            return $this->redirect($this->getSeoRepository()->getByEntity(SeoRepository::RESOURCE_URL_OFFER_CATEGORY, 509)->getMainAlias()); 
        } 
        if ($offer->isHidden()) { 
            throw $this->createNotFoundException(); 
        } 
        $response = new Response(); 
        $offerRateSchedule = $this->getOfferRateSchedule($request->getSession()); 
        if ($offerRateSchedule) { 
            if ($offerRateSchedule->getOffer()->getID() == $entityID) { 
                $request->getSession()->set(SiteController::LAST_OFFER_RATE_ACTIVITY_PARAMETER_NAME, time()); 
                $sql = 'delete from offer_rate_schedule where user_id = ' . (int)$this->getUser()->getID() . ' and offer_id = ' . (int)$entityID; 
                $entityManager->getConnection()->executeQuery($sql); 
            } else { 
                $data['offerRateSchedule'] = $offerRateSchedule; 
            } 
        } 
        $data['pastOffer'] = $pastOffer; 
        $data['todayPurchaseCount'] = $purchaseCountWithCorrectionDao->findLastDayByOfferId($offer->getID()); 
        if (!$pastOffer && $offer->getFreeCodesCount() > 0) { 
            $lastPurchaseTime = $offerRepository->getLastPurchaseTime($offer->getID()); 
            if ($lastPurchaseTime) { 
                $now = new \DateTime(); 
                $lastPurchaseInterval = $now->diff($lastPurchaseTime); 
                $lastPurchaseText = ''; 
                if ($lastPurchaseInterval->h > 0) { 
                    $lastPurchaseText = $lastPurchaseInterval->h . ' час' . CommonUtil::plural(['', 'а', 'ов'], $lastPurchaseInterval->h) . ' '; 
                } 
                $lastPurchaseMinutes = $lastPurchaseInterval->h == 0 && $lastPurchaseInterval->i < 2 ? 1 : $lastPurchaseInterval->i; 
                $lastPurchaseText .= $lastPurchaseMinutes . ' минут' . CommonUtil::plural(['у', 'ы', ''], $lastPurchaseMinutes) . ' назад'; 
                $data['lastPurchaseText'] = $lastPurchaseText; 
            } 
        } elseif (!self::getMobileDevice($request)) { 
            $data['relatedOfferListHtml'] = implode('', $this->getRelatedOffersTeasers($cacheService, $offer)); 
        } 
        $data['offer'] = $offer; 
        $data['galleryVideos'] = $videoPackageDtoGetter->getByOfferId($offer->getID()); 
 
        $userGeoLocation = $request->cookies->get(User::CURRENT_LOCATION_COOKIE, null); 
        if ($userGeoLocation) { 
            $userGeoLocation = explode(',', $userGeoLocation); 
        } 
        $data['offerGeoLocationData'] = $offerRepository->getOfferGeoLocationData($offer, $userGeoLocation, $imageService); 
        $data['freeCodesCount'] = 0; 
        $buyCodePopup = ""; 
 
        /** @var User $user */ 
        $user = $this->getUser(); 
        $data['offerIsFreeForUser'] = $offerRepository->isOfferFreeForUser($offer, $user); 
        $codeCost = $offerRepository->getCodeCost($offer); 
        if (!$pastOffer) { 
            if ($user && !$subscriptionService->isSubscriber($user) && !$data['offerIsFreeForUser']) { 
                $data['testSubscriptionGroupID'] = true; 
            } 
            $data['canBuyFromBalance'] = false; 
            $data['freeCodesCount'] = $offer->getFreeCodesCount(); 
            if ($offer->getID() != Offer::DREAMLAND_OFFER_ID) { 
                if (!$user) { 
                    $buyCodePopup = ".modal-auth"; 
                } else { 
                    if ($user->getFullBalance() >= $codeCost || $data['offerIsFreeForUser']) { 
                        $buyCodePopup = "#confirmBox"; 
                        $data['canBuyFromBalance'] = true; 
                    } 
                    if (isset($data['testSubscriptionGroupID'])) { 
                        $buyCodePopup = "#confirmBox"; 
                    } 
                } 
            } 
        } 
 
        if (!$serverFeatureStateChecker->isServerFeatureEnabled(SwitcherFeatures::REPLENISHMENT_BALANCE())) { 
            $buyCodePopup = "#confirmBox"; 
            $data['canBuyFromBalance'] = false; 
        } 
 
        if ($user) { 
            $subscription = $subscriptionService->getSubscription($user); 
            $data['allowedCodesCountBySubscription'] = $subscriptionService->isSubscriber($user) 
                ? $subscription->getNumberOfCodes() 
                : 0; 
        } 
 
        $buyButtonLabel = 'Получить скидку '; 
        if ($data['offer']->isOneOfOnlineOrderAllowedOnSite()) { 
            $buyButtonLabel = 'Получить промокод '; 
        } 
        if ($data['offerIsFreeForUser']) { 
            $buyButtonLabel = 'Бесплатный промокод'; 
        } else { 
            if ($offer->isActiveCurrencyCalculator() && is_numeric($offer->getDiscount())) { 
                $bankCurrency = $entityManager->getRepository(BankCurrency::class)->findOneBy(['currency' => $offer->getBankCurrency()->getCurrency()]); 
                if ($bankCurrency) { 
                    $buyButtonLabel .= round($bankCurrency->getRate() * $offer->getDiscount(), 2); 
                } 
            } else { 
                $buyButtonLabel .= $offer->getDiscount(); 
            } 
            $buyButtonLabel = mb_strtoupper($buyButtonLabel); 
        } 
        if ($entityID == Offer::DREAMLAND_OFFER_ID) { 
            $buyButtonLabel = 'Купить билет от ' . $offer->getOfferPrice($offer)  . ' руб.'; 
        } 
        $isMobile = CommonUtil::isMobileDevice($request); 
        $commentsCacheName = $isMobile ? CommentCacheService::MOBILE_CACHE_NAME : CommentCacheService::CACHE_NAME; 
        if ($data['currentPage'] == 1) { 
            $comments = $this->getComments($request, $bannerService, $entityID, Comment::TYPE_OFFER_COMMENT, 0, 0, 0, $commentsCacheName); 
        } else { 
            $comments = $this->getCommentsByOffer($entityID, $data['currentPage'], CommentRepository::COMMENTS_PER_PAGE, $isMobile); 
        } 
        $commentsAmount = $this->getCommentRepository()->getCommentsCountByEntityID($entityID, Comment::TYPE_OFFER_COMMENT); 
        $offerRating = $this->commentRatingDao->getCommentRatingByOfferId($offer->getID()); 
 
        $data['comments'] = $this->renderView($isMobile ? 'Slivki/mobile/comment/block.html.twig' : 'Slivki/comments/comments_list.html.twig', [ 
            'entityID' => $entityID, 
            'type' => Comment::TYPE_OFFER_COMMENT, 
            'comments' => $comments, 
            'commentsAmount' => $commentsAmount, 
            'showCommentsAmount' => true, 
            'rating' => $offerRating, 
        ]); 
        $data['items'] = []; //$offer->getItems(); 
        $dataItemsCount = count($data['items']); 
        if ($dataItemsCount > 0) { 
            $minOfferPrice = INF; 
            foreach ($data['items'] as $item) { 
                if ($item->getOfferPrice() < $minOfferPrice) { 
                    $minOfferPrice = $item->getOfferPrice(); 
                } 
            } 
            $buyButtonLabel = 'Купить билет от ' . number_format($minOfferPrice,2, ',', '') . ' руб.'; 
        } 
        $buyCodeBtnText = $offer->getBuyCodeButtonText(); 
        $buyCodeBtnText = $buyCodeBtnText ? $buyCodeBtnText : ''; 
        $data['buyButtonLabel'] = $buyCodeBtnText == '' ? mb_strtoupper(mb_substr($buyButtonLabel, 0, 1)) . mb_strtolower(mb_substr($buyButtonLabel, 1)) : $buyCodeBtnText; 
        if ($offer->isOneOfOnlineOrderAllowedOnSite()) { 
            $data['buyButtonLabelOnline'] = $offer->isBuyCodeDisable() ? 'Оформить сертификат онлайн' : 'Заказать онлайн'; 
        } 
 
        $data['isAllowedByOnlyCode'] =  $dataItemsCount == 0 || ($dataItemsCount > 0 && $offer->isAllowedBuyOnlyCode()); 
 
        $data['showGlobalcard'] = false; 
        $data['showGlobalcardFitness'] = false; 
 
        $data['tour3dName'] = ''; 
 
        $softCache = new SoftCache(OfferRepository::CACHE_NAME); 
        $cacheKey = 'alt-offers-box-' . $entityID; 
        $altOffersBox = $softCache->get($cacheKey); 
        if ($altOffersBox == SoftCache::LOCKED_KEY) { 
            $altOffersBox = $softCache->getDataForLockedKey($cacheKey, 30); 
        } else if (!$altOffersBox) { 
            $softCache->add($cacheKey, SoftCache::LOCKED_KEY, 30); 
            $cacheExpire = strtotime(date('Y-m-d 05:00:00', strtotime('+1 day'))) - time(); 
            $altOffersBox = ""; 
            $altOfferList = $this->getDoctrine()->getRepository(AlternativeOffer::class)->getByOfferID($entityID); 
            if ($altOfferList) { 
                if (count($altOfferList) < 3) { 
                    $altOffersBox = SoftCache::EMPTY_VALUE; 
                } else { 
                    if (count($altOfferList) < 6) { 
                        $altOfferList = array_slice($altOfferList, 0, 3); 
                    } 
                    foreach ($altOfferList as $altOffer) { 
                        $alternativeOffer = $offerCacheService->getOffer($altOffer->getAlternativeOfferID(), true); 
                        if ($alternativeOffer) { 
                            $altOffersBox .= $this->get('twig')->render('Slivki/offers/teaser.html.twig', ['offer' => $alternativeOffer, 'noLazyLoad' => true]); 
                        } else { 
                            $this->getLogger()->error('Alternative offer ' . $altOffer->getOfferID() . " not found in cache"); 
                            $altOffersBox = ""; 
                            break; 
                        } 
                    } 
                } 
            } 
            $softCache->set($cacheKey, $altOffersBox, $cacheExpire); 
            if ($altOffersBox == SoftCache::EMPTY_VALUE) { 
                $altOffersBox = ""; 
            } 
        } 
 
        if ($user && $subscriptionService->isSubscriber($user)) { 
            $buyCodePopup = '#confirmBox'; 
        } 
 
        $data['altOffersBox'] = $altOffersBox; 
        $data['geoLocations'] = $offer->getGeoLocations(); 
        $data['codeCost'] = $codeCost; 
        $data['buyCodePopup'] = $buyCodePopup; 
        $data['siteSettings'] = $this->getSiteSettings(); 
        $data['parentCategoryList'] = $this->getParentCategoryList($request, $offer->getDefaultCategoryID()); 
        $data['categoryURL'] = '/'; 
        $data['preview'] = $preview; 
        $data['commentsAmount'] = $this->getCommentRepository()->getCommentsCountByEntityID($offer->getID(), Comment::TYPE_OFFER_COMMENT); 
        $data['isOfferFavourite'] = $user ? $user->isOfferFavourite($offer) : false; 
        $data['detailMediaList'] = $offer->getDetailMedias(); 
        $data['foodExtensions'] = $offer->getFoodExtensions(); 
        $categoryRepository =  $this->getDoctrine()->getManager()->getRepository(Category::class); 
        $categoryID = $offer->getDefaultCategoryID(); 
        if ($categoryID) { 
            $category = $categoryRepository->findCached($categoryID); 
            if (!$category) { 
                $category['category'] = $categoryRepository->find($categoryID); 
            } 
            if ($category['category'] && $category['category']->isActive() && !$category['category']->isPast()) { 
                $data['categoryURL'] = $this->getSeoRepository()->getCategoryURL($category['category']); 
            } 
        } 
        $deviceType = self::getMobileDevice($request) ? self::DEVICE_TYPE_MOBILE : self::DEVICE_TYPE_DESKTOP; 
        $this->addVisit($entityID, Visit::TYPE_OFFER, $deviceType, $user, $request->getClientIp()); 
        if ($entityID == Offer::FITNESS_WORLD_OFFER_ID) { 
            $data['fitnessOffer'] = true; 
        } 
        $data['confirmedUserPhoneNumber'] = null; 
        $dateDiff = date_diff($offer->getActiveTill(), new \DateTime()); 
        $data['daysLeft'] = $dateDiff->days; 
        $data['hoursLeft'] = $dateDiff->format('%h'); 
        $data['minutesLeft'] = $dateDiff->format('%i'); 
        $data['usedCodesCount'] = $pastOffer 
            ? $offer->getUsedCodesCountForPastOffer() 
            : $purchaseCountWithCorrectionDao->findTotalByOfferId($offer->getID()) + $offer->getLastCodePoolStartPurchaseCount(); 
 
        $data['allCodesCount'] = $data['usedCodesCount'] + $offer->getFreeCodesCount(); 
        $data['ratingWithCount'] = $entityManager->getRepository(Comment::class)->getEntityRatingWithCount(Category::OFFER_CATEGORY_ID, $entityID); 
        $data['ratingPercentage'] = $data['ratingWithCount']['rating'] * 100 / 5; 
        $data['todayVisitCount'] = $offerRepository->getVisitCount($offer, true); 
        $data['visitCount'] = $entityManager->getRepository(Visit::class)->getVisitCount($entityID, Visit::TYPE_OFFER, 30, true); 
        $data['phoneNumbersCount'] = count($offer->getPhoneNumbers()); 
        if ($entityID == Offer::BOOKING_OFFER_ID) { 
            $entityOption = $entityManager->getRepository(EntityOption::class)->findOneBy([ 
                "entityID" => $entityID, 
                "entityTypeID" => EntityOption::BOOKING_RESERVED_DATES, 
                "name" => EntityOption::OPTION_BOOKING_DATES 
            ]); 
            if ($entityOption) { 
                $dateToFormat = []; 
                foreach (explode(',', $entityOption->getValue()) as $date) { 
                    $dateToFormat[] = date('Y-m-d', strtotime($date)); 
                } 
                $data['bookedDates'] = implode(',', $dateToFormat); 
            } 
            $data['bookingTypeList'] = Booking::BOOKING_TYPE_LIST; 
            $userCurrentPhone =  $user ? $user->getCurrentPhone() : null; 
            if ($userCurrentPhone and $userCurrentPhone->isConfirmed()) { 
                $data['confirmedUserPhoneNumber'] = $userCurrentPhone->getPhoneNumber(); 
            } 
        } 
        if ($user && $user->hasRole(UserGroup::STATISTICS_VIEWER)) { 
            $data['yesterdayShareClicks'] = $this->getShareClicks($entityID); 
        } 
 
        $utmMedium = trim(mb_strtolower($request->query->get('utm_medium', ''))); 
        if ($request->query->get('utm_source') == 'search_result' && $utmMedium != '') { 
            $searchQuery = $entityManager->getRepository(SearchQuery::class) 
                ->findOneByQuery(($utmMedium)); 
            if ($searchQuery) { 
                $searchStatistic = new SearchStatistic(); 
                $searchStatistic->setSearchQuery($searchQuery); 
                $searchStatistic->setEntityID($offer->getID()); 
                $searchStatistic->setEntityType(SearchStatistic::ENTITY_TYPE_OFFER); 
                if ($user) { 
                    $searchStatistic->setUserID($user->getID()); 
                } 
                $searchStatistic->setIpAddress($request->getClientIp()); 
                $searchStatistic->setRefferer($request->headers->get('referer')); 
                $entityManager->persist($searchStatistic); 
                $entityManager->flush(); 
            } 
        } 
        $codesCount = 1; 
        if ($entityID == Offer::DREAMLAND_OFFER_ID) { 
            $codesCount = 5; 
            if ($user) { 
                if ($entityManager->getRepository(User::class)->getDreamlandOption($user->getID())) { 
                    $codesCount = 1; 
                } 
            } 
        } 
        $data['codesCount'] = $codesCount; 
        $data['brandingBannerCategoryIDs'] = $categoryRepository->getParentCategoryIDsByOfferID($entityID); 
        $data['deliveryLink'] = '/delivery/select/' . $offer->getID(); 
        $onlineSettings = $offer->getOnlineOrderSettings(); 
        if (!$pastOffer && $offer->isOneOfOnlineOrderAllowedOnSite()) { 
            $data['hasDelivery'] = true; 
            $data['foodExtensions'] = []; 
            $data['items'] = []; 
            if (count($offer->getFoodExtensions())) { 
                $orderUtil = AbstractDelivery::instance($offer); 
                if ($orderUtil::SPLIT_PAYMENT) { 
                    $data['domain'] = $orderUtil::DOMAIN; 
                } 
                if ($onlineSettings && $onlineSettings->isSplitPayment()) { 
                    if ($onlineSettings->getDomain()) { 
                        $data['domain'] = trim($onlineSettings->getDomain()); 
                    } 
                } 
            } elseif ($giftCertificateDao->getCountActiveByOfferId($entityID)) { 
                $dql = 'select giftCertificate from Slivki:GiftCertificate giftCertificate where giftCertificate.offer = :offer'; 
                $giftCertificates = $entityManager->createQuery($dql) 
                    ->setParameter('offer', $offer)->getResult(); 
 
                if ($giftCertificates[0]->getLastActiveCodePool()) { 
                    $multiplePoolOfferFreeCodesCount = 0; 
                    $multiplePoolOfferUsedCodesCount = 0; 
                    /** @var GiftCertificate $giftCertificate */ 
                    foreach ($giftCertificates as $giftCertificate) { 
                        $giftCertificateCodePool = $giftCertificate->getLastActiveCodePool(); 
                        $multiplePoolOfferFreeCodesCount += $giftCertificateCodePool->getFreeCodesCount(); 
                        $multiplePoolOfferUsedCodesCount += $giftCertificateCodePool->getUsedCodesCount(); 
                    } 
                    $data['multiplePoolOfferFreeCodesCount'] = $multiplePoolOfferFreeCodesCount; 
                    $data['multiplePoolOfferUsedCodesCount'] = $multiplePoolOfferUsedCodesCount; 
                } 
                $data['deliveryLink'] = '/gift-certificate/select/' . $offer->getID(); 
                if ($onlineSettings && $onlineSettings->isSplitPayment()) { 
                    if ($onlineSettings->getDomain()) { 
                        $data['domain'] = trim($onlineSettings->getDomain()); 
                    } 
                } 
            } else { 
                $offer = $entityManager->merge($offer); 
                if (count($offer->getTireExtensions())) { 
                    $data['deliveryLink'] = '/online-zapis/' . $offer->getID(); 
                    if ($onlineSettings && $onlineSettings->isSplitPayment()) { 
                        if ($onlineSettings->getDomain()) { 
                            $data['domain'] = trim($onlineSettings->getDomain()); 
                        } 
                    } 
                } 
            } 
 
            $directorID = $offer->getDirectorID(); 
            if ($offer->getID() == Offer::DREAMLAND_OFFER_ID) { 
                $data['domain'] = 'mp'; 
            } else if ($entityID == 139498) { 
                $data['domain'] = 'deka'; 
            } else if ($entityID == 141075) { 
                $data['domain'] = 'whitelotus'; 
            } else if ($directorID == Director::MARSEL_DIRECTOR_ID) { 
                $data['domain'] = 'marsel'; 
            } else if ($directorID == Director::SHAH_DIRECTOR_ID) { 
                $data['domain'] = 'shah'; 
            } else if ($entityID == Offer::HEROPARK_OFFER_ID) { 
                $data['domain'] = 'heropark'; 
            } 
        } 
        if (isset($data['domain']) && $data['domain'] != '') { 
            $data['deliveryLink'] = 'https://' . $data['domain'] . str_replace('https://www.', '.', $this->getParameter('base_url')) . $data['deliveryLink']; 
        } 
        if ($offer->getExternalOfferLink() && $offer->getIsShowExternalOfferLink()) { 
            $data['deliveryLink'] = $offer->getExternalOfferLink(); 
        } 
        if ($user) { 
            $siteSettings = $this->getSiteSettings(); 
 
            $data['subscriptionPrice'] = $subscriptionService->isSubscriptionFinished($user) ? $siteSettings->getSubscriptionPrice() : $siteSettings->getSubscriptionFirstPayment(); 
            $data['subscriptionFullPrice'] = $siteSettings->getSubscriptionPrice(); 
            $data['subscriptionTerm'] = $subscriptionService->isSubscriptionFinished($user) ? 30 : 7; 
        } 
 
        if ($preview) { 
            $data['robotsMeta'] = 'noindex, follow'; 
        } 
 
        if (!$offer->hasFreeCodes()) { 
            $data['deliveryLink'] = 'javascript:void(0);'; 
        } 
 
        $data['hadSubscription'] = null !== $user ? $subscriptionService->isExistBySubscriber($user) : false; 
        $data['codeCostInCurrency'] = $offer->getSumInCurrency((float) $codeCost); 
        $data['rating'] = $offerRating; 
        $data['defaultPickup'] = $onlineSettings && $onlineSettings->isDefaultPickup(); 
 
        $view = $isMobile ? 'Slivki/mobile/offer/details.html.twig' : 'Slivki/offers/details.html.twig'; 
        $response->setContent($this->renderView($view, $data)); 
 
        return $response; 
    } 
 
    /** 
     * @Route("/offer/comment/get/{offerID}/{page}") 
     */ 
    public function getOfferComments(Request $request, OfferCacheService $offerCacheService, $offerID, $page) { 
        $offer = $offerCacheService->getOffer($offerID, false, true); 
        if (!$offer) { 
            return new Response(); 
        } 
        $isMobile = CommonUtil::isMobileDevice($request); 
        return new Response($this->getCommentsByOffer($offer->getID(), $page, CommentRepository::COMMENTS_PER_PAGE, $isMobile)); 
    } 
 
    private function getCommentsByOffer($offerID, $page, $perPage, $isMobile) { 
        $entityManager = $this->getDoctrine()->getManager(); 
        $offset = ($page - 1) * $perPage; 
        $commentList = $entityManager->getRepository(Comment::class)->getCommentsByOfferIDReversed($offerID, $offset, $perPage); 
        $commentsAmount = $this->getCommentRepository()->getCommentsCountByEntityID($offerID, Comment::TYPE_OFFER_COMMENT); 
        return  $this->renderView($isMobile ? 'Slivki/mobile/comment/list.html.twig' : 'Slivki/comments/comments.html.twig', [ 
            'comments' => $commentList, 
            'pagination' => $this->renderView('Slivki/pagination.html.twig', [ 
                'paginationID' => 'offerCommentPagination', 
                'current' => $page, 
                'total' => ceil($commentsAmount/$perPage), 
                'url' => $entityManager->getRepository(Seo::class)->getOfferURL($offerID)->getMainAlias() . '?page=' 
            ]), 
            'showBanners' => true, 
            'hasMore' => false 
        ]); 
    } 
 
    /** 
     * @Route("/additional_offer_details/{entityID}/{offerPreview}") 
     */ 
    public function additionalOfferDetailsAction(Request $request, OfferCacheService $offerCacheService, $entityID, $offerPreview = false) { 
        $entityManager = $this->getDoctrine()->getManager(); 
        $offerRepository = $this->getOfferRepository(); 
        $offer = $offerCacheService->getOffer($entityID, false, true); 
 
        $pastOffer = false; 
        $timeNow = new \DateTime(); 
        if (!$offer || $offerPreview) { 
            $offer = $offerRepository->find($entityID); 
            if (!$offer || ((!$offer->isInVisiblePeriod() || !$offer->isActive()) && !$offerPreview)) { 
                throw $this->createNotFoundException(); 
            } 
            $mediaRepository = $this->getMediaRepository(); 
            $offer->setDetailMeidas($mediaRepository->getOfferDetailMedias($offer->getID())); 
            $offer->setShopMedias($mediaRepository->getOfferShopMedias($offer->getID())); 
        } 
        $data['parentCategoryList'] = null; 
        $data['offer'] = $offer; 
        $data['tour3dName'] = ''; 
 
        $softCache = new SoftCache(OfferRepository::CACHE_NAME); 
        $cacheKey = 'alt-offers-box-1-' . $entityID; 
        $altOffersBox = $softCache->get($cacheKey); 
        if ($altOffersBox == SoftCache::LOCKED_KEY) { 
            $altOffersBox = $softCache->getDataForLockedKey($cacheKey, 30); 
        } else if (!$altOffersBox) { 
            $softCache->add($cacheKey, SoftCache::LOCKED_KEY, 30); 
            $cacheExpire = strtotime(date('Y-m-d 05:00:00', strtotime('+1 day'))) - time(); 
            $altOffersBox = ""; 
            $altOfferList = $this->getDoctrine()->getRepository(AlternativeOffer::class)->getByOfferID($entityID); 
            if ($altOfferList) { 
                if (count($altOfferList) < 3) { 
                    $altOffersBox = SoftCache::EMPTY_VALUE; 
                } else { 
                    if (count($altOfferList) < 6) { 
                        $altOfferList = array_slice($altOfferList, 0, 3); 
                    } 
                    foreach ($altOfferList as $altOffer) { 
                        $alternativeOffer = $offerCacheService->getOffer($altOffer->getAlternativeOfferID(), true); 
                        if ($alternativeOffer) { 
                            $altOffersBox .= $this->get('twig')->render('Slivki/offers/teaser.html.twig', ['offer' => $alternativeOffer, 'noLazyLoad' => true]); 
                        } else { 
                            $this->getLogger()->error('Alternative offer ' . $altOffer->getOfferID() . " not found in cache"); 
                            $altOffersBox = ""; 
                            break; 
                        } 
                    } 
                } 
            } 
            $softCache->set($cacheKey, $altOffersBox, $cacheExpire); 
            if ($altOffersBox == SoftCache::EMPTY_VALUE) { 
                $altOffersBox = ""; 
            } 
        } 
        $data['altOffersBox'] = $altOffersBox; 
        $view = $request->query->get('offerCondition') ? 'Slivki/offers/condition.html.twig' : 'Slivki/offers/additional_offer_details.html.twig'; 
        if (self::getMobileDevice($request)) { 
            $view = 'Slivki/mobile/offer/description.html.twig'; 
        } 
        return $this->render($view, $data); 
    } 
 
    /** 
     * @Route("/offer_location/{entityID}") 
     */ 
    public function offerLocationAction(Request $request, OfferCacheService $offerCacheService, $entityID) { 
        $offerRepository = $this->getOfferRepository(); 
        $offer = $offerCacheService->getOffer($entityID, false, true); 
 
        if (!$offer) { 
            $offer = $offerRepository->find($entityID); 
            if (!$offer) { 
                throw $this->createNotFoundException(); 
            } 
            $mediaRepository = $this->getMediaRepository(); 
            $offer->setDetailMeidas($mediaRepository->getOfferDetailMedias($offer->getID())); 
            $offer->setShopMedias($mediaRepository->getOfferShopMedias($offer->getID())); 
        } 
 
        $data['offer'] = $offer; 
        $data['geoLocations'] = $offer->getGeoLocations(); 
 
        return $this->render('Slivki/offers/location.html.twig', $data); 
    } 
 
    /** 
     * @Route("/get_comment_box/{type}/{entityID}/{mobileCache}") 
     */ 
    public function getCommentBoxAction(Request $request, BannerService $bannerService, $type, $entityID, $mobileCache = null) { 
        if ($entityID == 140503) { 
            Logger::instance('COMMENT-DEBUG')->info('start'); 
        } 
        ini_set('memory_limit', '512M'); 
        $directorID = 0; 
        if ($type == Comment::TYPE_OFFER_COMMENT) { 
            $offer = $this->getDoctrine()->getManager()->find(Offer::class, $entityID); 
            if ($offer->getDirectors()->count() > 0) { 
                $directorID = $offer->getDirectors()->first()->getID(); 
            } 
        } 
        if ($entityID == 140503) { 
            Logger::instance('COMMENT-DEBUG')->info('05'); 
        } 
        $cacheName = $mobileCache ? CommentCacheService::MOBILE_CACHE_NAME : CommentCacheService::CACHE_NAME; 
        $data = [ 
            'entityID' => $entityID, 
            'type' => $type, 
            'comments' => $this->getComments($request, $bannerService, $entityID, $type, 0, 0, $directorID, $cacheName), 
            'commentsAmount' => $this->getCommentRepository()->getCommentsCountByEntityID($entityID, $type), 
            'showCommentsAmount' => $type == Comment::TYPE_SALE_COMMENT ? false : true 
        ]; 
        if ($entityID == 140503) { 
            Logger::instance('COMMENT-DEBUG')->info('1'); 
        } 
        $view = 'Slivki/comments/comments_list.html.twig'; 
        if ($entityID == 140503) { 
            Logger::instance('COMMENT-DEBUG')->info('2'); 
        } 
        if ($cacheName == CommentRepository::MOBILE_CACHE_NAME) { 
            $view = 'Slivki/mobile/comment/block.html.twig'; 
        } 
        if ($entityID == 140503) { 
            Logger::instance('COMMENT-DEBUG')->info('3'); 
        } 
        $response = new Response(); 
        $response->setContent($this->get('twig')->render($view, $data)); 
        if ($entityID == 140503) { 
            Logger::instance('COMMENT-DEBUG')->info('status code ' . $response->getStatusCode()); 
        } 
        return $response; 
    } 
 
    /** 
     * @Route("/landing") 
     */ 
    public function mobileLandingAction( 
        Request $request, 
        OfferCacheService $offerCacheService, 
        SubscriptionService $subscriptionService, 
        DeviceTypeService $deviceTypeService, 
        ServerFeatureStateChecker $serverFeatureStateChecker 
    ): Response { 
        if (!$deviceTypeService->isMobileDevice($request) || !$serverFeatureStateChecker->isServerFeatureEnabled(SwitcherFeatures::ALLOW_MOBILE_LANDING_PAGE())) { 
            return $this->redirectToRoute('homepage'); 
        } 
 
        $response = new Response(); 
        $entityManager = $this->getDoctrine()->getManager(); 
        $user = $this->getUser(); 
        $carouselOffersIDs = [Dominos::OFFER_ID, SushiHouse::OFFER_ID, Offer::KFC_OFFER_ID, Offer::FREESTYLE_OFFER_ID, Dodo::OFFER_ID]; 
        $data['recomended'] = true; 
        if ($user) { 
            $sql = 'select entity_id from visit where user_id = ' . $user->getID() 
                . ' and entity_type_id = ' . Category::OFFER_CATEGORY_ID 
                . ' group by 1 order by max(created_on) desc limit 10'; 
            $visitedOfferIDs = $this->getDoctrine()->getManager()->getConnection()->executeQuery($sql)->fetchAll(\PDO::FETCH_COLUMN); 
            if (!empty($visitedOfferIDs)) { 
                $carouselOffersIDs = $visitedOfferIDs; 
                $data['recomended'] = false; 
            } 
        } 
        $carouselOffers = $offerCacheService->getOffers($carouselOffersIDs); 
        if ($data['recomended']) { 
            shuffle($carouselOffers); 
        } 
        $data['carouselOffers'] = $carouselOffers; 
        $data['categoryList'] = $this->getCategoryRepository()->getUserFavouriteCategories($user); 
        $data['showWatchList'] = $user && $this->getUser() && $this->getVisitedByUserCount($user->getID()) > 0; 
 
        $abTestViews = [ 
            'Slivki/mobile/landing_new.html.twig' 
        ]; 
 
        $landingABCookieName = 'landingab'; 
        $landingABCookieValue = $request->cookies->get($landingABCookieName); 
        if (!$landingABCookieValue) { 
            $landingABCookieValue = array_rand($abTestViews); 
        } else { 
            $landingABCookieValue = (int)$landingABCookieValue; 
            $landingABCookieValue++; 
            if ($landingABCookieValue >= count($abTestViews)) { 
                $landingABCookieValue = 0; 
            } 
        } 
        $landingABCookie = Cookie::create($landingABCookieName, $landingABCookieValue, time() + 315360000, '/', $this->getParameter('base_domain')); 
        $response->headers->setCookie($landingABCookie); 
 
        $data['subscription'] = null !== $user ? $subscriptionService->getSubscription($user) : null; 
        $data['hadSubscription'] = null !== $user ? $subscriptionService->isExistBySubscriber($user) : null; 
 
        $landingBannerRepository = $entityManager->getRepository(MobileLandingBanner::class); 
        $bannerList = $landingBannerRepository->findBy(['active' => true], ['position' => 'ASC']); 
        $data['landingBannerHtmlTop'] = $this->get('twig')->render('Slivki/banners/landing_banner_top.html.twig', ['bannerList' => $bannerList]); 
        $data['landingBannerHtmlBottom'] = $this->get('twig')->render('Slivki/banners/landing_banner_bottom.html.twig', ['bannerList' => $bannerList]); 
        $response->setContent($this->renderView($abTestViews[$landingABCookieValue], $data)); 
 
        return $response; 
    } 
 
    private function getVisitedByUserCount($userID) { 
        $sql = 'select count(distinct entity_id) from visit where user_id = ' . $userID . ' and entity_type_id = ' . Category::OFFER_CATEGORY_ID; 
        return $this->getDoctrine()->getManager()->getConnection()->executeQuery($sql)->fetchColumn(); 
    } 
 
    public function getRelatedOffersTeasers(CacheService $cacheService, $offerID) { 
        $entityManager = $this->getDoctrine()->getManager(); 
        $offer = $entityManager->find(Offer::class, $offerID); 
        $defaultCategoryID = $offer->getDefaultCategoryID(); 
        if (empty($defaultCategoryID)) { 
            return ''; 
        } 
 
        $entityIDList = $this->offerDao->findActiveOffersByCategoryIds([$defaultCategoryID]); 
        if (0 == count($entityIDList)) { 
            return ''; 
        } 
 
        $sql = 'select entity_id, purchase_count_recent from purchase_count where entity_id in (' . implode(',', $entityIDList) . ') order by 2 desc limit 12;'; 
        try { 
            $purschaseCountList = $entityManager->getConnection()->executeQuery($sql)->fetchAll(\PDO::FETCH_KEY_PAIR); 
        } catch (\PDOException $e) { 
            Logger::instance('Related offers')->info($offerID); 
        } 
        return $cacheService->getTeaserList(array_keys($purschaseCountList), false); 
    } 
 
    /** 
     * @Route("/comments/add_like/{commentID}") 
     */ 
    public function addLike($commentID, Request $request) { 
        if ($this->isGranted(UserGroup::COMMENTS_BANNED_ROLE_NAME)) { 
            return new Response(); 
        } 
        $user = $this->getUser(); 
        if(!$user) { 
            return new Response(json_encode(['error' => 1])); 
        } 
        /** @var Comment $comment */ 
        $comment = $this->getCommentRepository()->find($commentID); 
        $like = new CommentLike(); 
        $like->setUserID($user->getID()); 
        $like->setVote((bool)$request->request->get('vote')); 
        $comment->addLike($like); 
        $entityManager = $this->getDoctrine()->getManager(); 
        $entityManager->flush(); 
        $result = $comment->getLikesAmount(); 
        $this->resetCommentsCache($comment->getEntityID(), $comment->getTypeID()); 
        return new Response(json_encode($result)); 
    } 
 
    private function validateComment(Request $request) { 
        $commentText = trim($request->request->get('comment', '')); 
        if($commentText == '') { 
            $result = 'Отзыв не может быть пустым'; 
            return $result; 
        } 
        return true; 
    } 
 
    /** 
     * @Route("/ostavit-otziv/{typeID}/{entityID}/{parentID}", defaults = {"parentID" = 0}) 
     */ 
    public function addComment($typeID, $entityID, $parentID, Request $request) { 
        if (!self::getMobileDevice($request)) { 
            return $this->redirectToRoute('homepage'); 
        } 
        $typeID = (int)$typeID; 
        $entityID = (int)$entityID; 
        $parentID = (int)$parentID; 
        if (!in_array($typeID, [Comment::TYPE_OFFER_COMMENT, Comment::TYPE_SALE_COMMENT, Comment::TYPE_MALL_BRAND_COMMENT, Comment::TYPE_DIRECTOR_COMMENT])) { 
            return $this->redirectToRoute('homepage'); 
        } 
 
        if ($parentID > 0) { 
            $parentComment = $this->getCommentRepository()->find($parentID); 
            if (!$parentComment || $parentComment->getEntityID() != $entityID) { 
                return $this->redirectToRoute('homepage'); 
            } 
        } 
 
        return $this->render('Slivki/mobile/comment/add.html.twig', [ 
            'typeID' => $typeID, 
            'entityID' => $entityID, 
            'parentID' => $parentID, 
            'referer' => $request->headers->get('referer', '/') 
        ]); 
    } 
 
    /** 
     * @Route("/redaktirovat-otziv/{commentID}") 
     */ 
    public function editComment($commentID, Request $request) { 
        if (!self::getMobileDevice($request)) { 
            return $this->redirectToRoute('homepage'); 
        } 
        $commentRepository = $this->getCommentRepository(); 
        /** @var Comment $comment */ 
        $comment = $commentRepository->find($commentID); 
        if (!$comment) { 
            return $this->redirectToRoute('homepage'); 
        } 
        $user = $this->getUser(); 
        if (!$user || !($user->getID() == $comment->getUser()->getID())) { 
            return $this->redirectToRoute('homepage'); 
        } 
        $commentRating = $comment->getRating(); 
        return $this->render('Slivki/mobile/comment/edit.html.twig', [ 
            'comment' => $comment, 
            'commentRating' => $commentRating, 
            'isUserAllowedToRate' => $commentRating > 0, 
            'referer' => $request->headers->get('referer', '/') 
        ]); 
    } 
 
    /** 
     * @Route("/udalit-otziv/{commentID}") 
     */ 
    public function deleteComment($commentID, Request $request) { 
        if (!self::getMobileDevice($request)) { 
            return $this->redirectToRoute('homepage'); 
        } 
        $comment = $this->getCommentRepository()->find($commentID); 
        if (!$comment) { 
            return $this->redirectToRoute('homepage'); 
        } 
        $user = $this->getUser(); 
        if (!$user || !($user->getID() == $comment->getUser()->getID())) { 
            return $this->redirectToRoute('homepage'); 
        } 
        return $this->render('Slivki/mobile/comment/delete.html.twig', [ 
            'comment' => $comment, 
            'referer' => $request->headers->get('referer', '/') 
        ]); 
    } 
 
    /** 
     * @Route("/comments/add/{type}/{entityID}") 
     */ 
    public function commentAdd( 
        int $type, 
        int $entityID, 
        Request $request, 
        Mailer $mailer, 
        ServerFeatureStateChecker $serverFeatureStateChecker, 
        VoiceMessageUploader $voiceMessageUploader 
    ): Response { 
        $user = $this->getUser (); 
        if (!$user) { 
            return new Response('Войдите, чтобы мы могли учесть Ваше мнение!'); 
        } 
 
        if ($this->isGranted(UserGroup::COMMENTS_BANNED_ROLE_NAME)) { 
            return new Response('Добавление комментариев заблокировано администратором'); 
        } 
 
        $validateCommentResult = $this->validateComment($request); 
        if ($validateCommentResult !== true) { 
            return new Response($validateCommentResult); 
        } 
 
        $parentCommentID = (int)$request->request->get('parentVoteId'); 
        $commentText = trim($request->request->get('comment', '')); 
        if (Censure::parse($commentText)) { 
            $response = new Response(); 
            $response->setStatusCode(406); 
            return $response; 
        } 
 
        $entityManager = $this->getDoctrine()->getManager(); 
 
        $userPhone = $user->getCurrentPhone(); 
        $confirmedPhone = true; 
        if ($serverFeatureStateChecker->isServerFeatureEnabled(SwitcherFeatures::NEED_CONFIRM_PHONE_TO_COMMENT()) 
            && (!$userPhone || !$userPhone->isConfirmed() || !$userPhone->isBelorussian())) { 
            $confirmedPhone = false; 
        } 
 
        if ($type == Comment::TYPE_OFFER_COMMENT) { 
            $sql = 'DELETE FROM offer_rate_schedule WHERE user_id = ' . (int)$user->getID() . ' AND offer_id = ' . (int)$entityID; 
            $entityManager->getConnection()->executeQuery($sql); 
        } 
 
        $parentComment = $this->getCommentRepository()->find($parentCommentID); 
        $comment = new Comment(); 
        $comment->setComment($this->prepareCommentText($commentText)); 
        $rating = $request->request->get('actionRating'); 
        $comment->setRating($rating); 
 
        if ($parentComment) { 
            $comment->setParentComment($parentComment); 
        } 
 
        $comment->setEntityID($entityID); 
        $comment->setHidden(false); 
        $comment->setTypeID($type); 
        $comment->setMobileVersion(CommonUtil::isMobileDevice($request)); 
        $comment->setChecked(false); 
        $comment->setUser ($user); 
        $comment->setConfirmedPhone($confirmedPhone); 
        if ($userPhone instanceof UserPhone) { 
            $comment->setPhone($userPhone->getPhoneNumber()); 
        } 
 
        $comment->setAllowToContact($request->request->getBoolean('allowToContact')); 
 
        $voiceFile = $request->files->get('voice_message'); 
        if ($voiceFile) { 
            $voicePath = $voiceMessageUploader->upload($voiceFile); 
            if ($voicePath !== null) { 
                $comment->setVoicePath($voicePath); 
            } 
        } 
 
        $entityManager->persist($comment); 
 
        $session = $request->getSession(); 
        $userCommentImages = $session->get('userCommentImages', []); 
 
        foreach ($userCommentImages as $key => $value) { 
            $media = new Media\CommentMedia(); 
            $media->setMediaType($entityManager->getRepository(MediaType::class)->find(MediaType::TYPE_USER_VOTE_IMAGE_ID)); 
            $media->setPath(MediaType::TYPE_USER_VOTE_IMAGE_PATH); 
            $media->setName($value); 
            $media->setSortOrder($key); 
            $comment->addMedia($media); 
        } 
        $session->set('userCommentImages', []); 
 
        $entityManager->flush($comment); 
 
        if ($type == Comment::TYPE_OFFER_COMMENT && $confirmedPhone) { 
            $offer = $this->getOfferRepository()->find($entityID); 
            $this->sendOfferCommentNotice($mailer, $offer, $comment, $parentComment); 
        } 
        $this->resetCommentsCache($entityID, $type); 
        return new Response($confirmedPhone ? '<p class="mb-3" style="font-size: 30px;">😊 </p><strong style="font-family:SF Pro Rounded Bold">Благодарим за оставленный отзыв!</strong> <br><br> <p class="mb-4" style="font-family:SF Pro Rounded;font-weight: 100;">Такие отзывы, как ваш, помогают другим людям находить самые лучшие акции и интересные места</p>' : 'confirm'); 
    } 
 
 
    /** 
     * @Route("/comments-live") 
     * @Route("/comments-live/{alias}") 
     */ 
    public function commentsLiveRedirect($alias = null) { 
        $routeName = 'commentsLive'; 
        $routeParameters = []; 
        if ($alias != '') { 
            $routeName = 'commentsByCategory'; 
            $routeParameters['alias'] = $alias; 
        } 
        return $this->redirectToRoute($routeName, $routeParameters, 301); 
    } 
    /** 
     * @Route("/otzyvy", name="commentsLive") 
     */ 
    public function commentsLiveAction(Request $request) { //TODO: total refactoring commentsLiveAction and commentsByCategory 
        $isMobileDevice = self::getMobileDevice($request); 
        $commentRepository = $this->getCommentRepository(); 
        $topMenu = $commentRepository->getTopMenu(); 
        $data['offerRateSchedule'] = $this->getOfferRateSchedule($request->getSession()); 
        $data['comments'] = $commentRepository->getLiveComments(20); 
        $data['isLiveComments'] = 1; 
        $data['hasMore'] = true; 
        $data['commentsAmount'] = 20; 
        $comments = $this->get('twig')->render($isMobileDevice ? 'Slivki/mobile/comment/list.html.twig' : 'Slivki/comments/comments.html.twig', $data); 
 
        $deviceType = self::getMobileDevice($request) ? self::DEVICE_TYPE_MOBILE : self::DEVICE_TYPE_DESKTOP; 
        $this->addVisit(0, Visit::TYPE_COMMENTS_MAIN_PAGE, $deviceType, $this->getUser(), $request->getClientIp(), $request->headers->get('referer', '')); 
 
        return $this->render($isMobileDevice ? 'Slivki/mobile/comment/index.html.twig' : 'Slivki/comments/comments_live.html.twig', [ 
            'comments' => $comments, 
            'topMenu' => $topMenu 
        ]); 
    } 
 
    /** 
     * @Route("/otzyvy/{alias}", name="commentsByCategory") 
     */ 
    public function commentsByCategoryAction(Request $request, $alias = null) { 
        $page = $request->query->get('page', 1); 
        $isMobileDevice = self::getMobileDevice($request); 
        $seo = $request->attributes->get(SiteController::PARAMETER_META_INFO); 
        if (!$seo && $alias) { 
            $seo = $this->getSeoRepository()->findOneBy(['mainAlias' => '/' . $alias]); 
        } 
        if (!$seo) { 
            return $this->redirectToRoute('commentsLive'); 
        } 
        $seoType = $seo->getResourceURL(); 
        $entityID = $seo->getEntityID(); 
        $commentRepository = $this->getCommentRepository(); 
        switch($seoType) { 
            case SeoRepository::RESOURCE_URL_CATEGORY_COMMENTS: 
            case SeoRepository::RESOURCE_URL_OFFER_CATEGORY: 
                $categoryRepository = $this->getCategoryRepository(); 
                $categoryCached = $categoryRepository->findCached($entityID); 
                if(!$categoryCached) { 
                    return $this->redirectToRoute('commentsLive'); 
                } 
                $category = $categoryCached['category']; 
                $subCategories = $categoryRepository->getSubCategories($category); 
 
                $data['isLiveComments'] = 1; 
                $data['hasMore'] = true; 
                $data['categoryID'] = $entityID; 
                $data['commentsAmount'] = 10; 
                if ($category->getTypeID() == Category::SUPPLIER_CATEGORY_TYPE) { 
                    $data['comments'] = $commentRepository->getOfferCategoryComments($entityID, $page); 
                    $data['hasMore'] = false; 
                } else { 
                    $data['comments'] = $commentRepository->getCommentsByCategoryID($category->getID(), $data['commentsAmount']); 
                } 
                $comments = $this->renderView($isMobileDevice ? 'Slivki/mobile/comment/list.html.twig' : 'Slivki/comments/comments.html.twig', $data); 
                $title = mb_strtoupper('<h1>ОТЗЫВЫ ' . $category->getName() . '</h1>'); 
 
                return $this->render($isMobileDevice ? 'Slivki/mobile/comment/index.html.twig' : 'Slivki/comments/comments_live.html.twig', [ 
                    'commentsCount' => $commentRepository->getOfferCategoryCommentsCount($category->getID()), 
                    'page' => $page, 
                    'comments' => $comments, 
                    'subCategories' => $subCategories, 
                    'title' => $title, 
                    'categoryID' => $entityID, 
                    'category' => $category, 
                    'parentCategories' => $categoryRepository->getCategoryParentList($category) 
                ]); 
 
                break; 
            case SeoRepository::RESOURCE_URL_OFFER_DETAILS: 
            case SeoRepository::RESOURCE_URL_SALE_DETAILS: 
                return $this->redirect('/' . $alias, 301); 
                break; 
        } 
 
        return $this->redirect(CityRepository::$mainPageURL); 
    } 
 
    /** 
     * @Route("/comments/load") 
     */ 
    public function commentLoad(Request $request, BannerService $bannerService) { 
        $offerID = $request->request->get('marketActionOID'); 
        $typeID = $request->request->get('typeID'); 
        $lastCommentID = $request->request->get('lastCommentOID'); 
        $categoryID = $request->request->getInt('categoryID'); 
        $directorID = $request->request->getInt('directorID'); 
        $cacheName = $request->request->getInt('isMobileCache') == 0 ? CommentCacheService::CACHE_NAME : CommentCacheService::MOBILE_CACHE_NAME; 
        return new Response($this->getComments($request, $bannerService, $offerID, $typeID, $lastCommentID, $categoryID, $directorID, $cacheName)); 
    } 
 
    /** 
     * @Route("/comments/get_by_user/{userID}") 
     */ 
    public function commentsGetByUser($userID, Request $request) { 
        $offerID = $request->query->get('offerID'); 
        if (!$offerID) { 
            return new Response(); 
        } 
        $dql = "select comment from Slivki:Comment comment  
            where comment.userID = :userID and comment.entityID = :offerID  
            and (comment.hidden = false or comment.hidden is null) 
            and (comment.deleted = false or comment.deleted is null) 
            and comment.confirmedPhone = true 
            order by comment.createdOn desc"; 
        $data['comments'] = $this->getDoctrine()->getManager()->createQuery($dql) 
            ->setParameter('userID', $userID) 
            ->setParameter('offerID', $offerID) 
            ->getResult(); 
        return $this->render('Slivki/comments/comments_by_user.html.twig', $data); 
    } 
 
    /** 
     * @Route("/comment/image_upload") 
     */ 
    public function commentImageUpload(Request $request, KernelInterface $kernel) { 
        $imageFolder = $kernel->getProjectDir() . '/public' . ImageService::MEDIA_ROOT . ImageService::INITIAL_PATH . MediaType::TYPE_USER_VOTE_IMAGE_PATH; 
        $uploadedFile = $request->files->get('imageUploadForm'); 
        if ($uploadedFile) { 
            if (!in_array(mb_strtolower($uploadedFile->getClientOriginalExtension()), ['jpg', 'png', 'gif', 'jpeg'])) { 
                return new Response("error=true;result=Разрешены только .jpg, .jpeg, .png или .gif изображения"); 
            }; 
 
            $fs = new Filesystem(); 
            $newFileName = time() . '_' . $uploadedFile->getClientOriginalName(); 
 
            while($fs->exists($imageFolder . $newFileName)) { 
                $newFileName = time() . '_' . $newFileName; 
            } 
 
            $uploadedFile->move($imageFolder, $newFileName); 
 
            $session = $request->getSession(); 
            $userCommentImages = $session->get('userCommentImages', []); 
            $userCommentImages[] = $newFileName; 
            $session->set('userCommentImages', $userCommentImages); 
 
            return new Response("error=false;result=" . ImageService::MEDIA_ROOT . ImageService::INITIAL_PATH . MediaType::TYPE_USER_VOTE_IMAGE_PATH . $newFileName); 
        } else { 
            return new Response("error=true;result=Error"); 
        } 
    } 
 
    /** 
     * @Route("/comment/image_remove") 
     */ 
    public function commentImageRemove(Request $request) { 
        $imageIndex = $request->request->get('imageIndex'); 
        $mediaID = $request->request->getInt('id'); 
        if ($mediaID != 0) { 
            $entityManager = $this->getDoctrine()->getManager(); 
            $media = $entityManager->getRepository(Media\CommentMedia::class)->find($mediaID); 
            if ($media && $this->getUser() && $media->getComment()->getUserID() == $this->getUser()->getID()) { 
                $entityManager->remove($media); 
                $entityManager->flush(); 
            } 
        } 
        $session = $request->getSession(); 
        $userCommentImages = $session->get('userCommentImages', []); 
        if (isset($userCommentImages[$imageIndex])) { 
            unset($userCommentImages[$imageIndex]); 
        } 
        $session->set('userCommentImages', array_values($userCommentImages)); 
        return new Response("error=false;result="); 
    } 
 
    /** 
     * @Route("/comment/get/{commentID}") 
     */ 
    public function commentGet($commentID) { 
        $comment = $this->getCommentRepository()->find($commentID); 
        return new Response(json_encode([ 
            'comment' => $comment, 
            'commentMediasHtml' => $this->renderView('Slivki/comments/medias_preview.html.twig', ['medias' => $comment->getMedias()]) 
        ])); 
    } 
 
    /** 
     * @Route("/comment/is_user_allowed_to_rate/{typeID}/{entityID}") 
     */ 
    public function isUserAllowedToRate($typeID, $entityID) { 
        $user = $this->getUser(); 
        if (!$user) { 
            return new JsonResponse(json_encode(false)); 
        } 
        return new Response(json_encode($this->getCommentRepository()->isUserAllowedToRate($user->getID(), $entityID, $typeID))); 
    } 
 
    /** 
     * @Route("/comment/edit/{commentID}") 
     */ 
    public function commentEdit($commentID, Request $request) { 
        $entityManager = $this->getDoctrine()->getManager(); 
        /** @var \Slivki\Entity\Comment $comment */ 
        $comment = $this->getCommentRepository()->find($commentID); 
        if (!$comment) { 
            return new Response(''); 
        } 
        if ($comment->getCreatedOn()->format('U') < strtotime('-7 days') || $comment->getUserID() != $this->getUser()->getID()) { 
            return new Response(''); 
        } 
        $commentText = trim($request->request->get('comment', '')); 
        if (Censure::parse($commentText)) { 
            $response = new Response(); 
            $response->setStatusCode(406); 
            return $response; 
        } 
        if ($comment->getRating() > 0) { 
            $rating = $request->request->getInt('rating'); 
            if ($rating > $comment->getRating()) { 
                $comment->setRating($rating); 
            } 
        } 
        $commentChildren = $comment->getChildren()->toArray(); 
        if (empty($commentChildren)) { 
            $comment->setComment($this->prepareCommentText($commentText)); 
            $comment->setChecked(false); 
        } 
        $comment->setAllowToContact($request->request->getBoolean('allowToContact')); 
 
        $session = $request->getSession(); 
        $userCommentImages = $session->get('userCommentImages', []); 
        $mediaTypeRepository = $entityManager->getRepository(MediaType::class); 
        foreach ($userCommentImages as $key=>$value) { 
            $media = new Media\CommentMedia(); 
            $media->setMediaType($mediaTypeRepository->find(MediaType::TYPE_USER_VOTE_IMAGE_ID)); 
            $media->setPath(MediaType::TYPE_USER_VOTE_IMAGE_PATH); 
            $media->setName($value); 
            $media->setSortOrder($key); 
            $comment->addMedia($media); 
        } 
        $session->set('userCommentImages', []); 
 
        $entityManager->flush(); 
        $this->resetCommentsCache($comment->getEntityID(), $comment->getTypeID()); 
        $comment->setComment(nl2br($comment->getComment())); 
        return new Response(json_encode([ 
            'comment' => $comment, 
            'mediasHtml' => $this->renderView('Slivki/comments/medias.html.twig', ['medias' => $comment->getMedias()->toArray()]) 
        ])); 
    } 
 
    /** 
     * @Route("/comment/delete/{commentID}") 
     */ 
    public function commentDelete($commentID, Request $request) { 
        /** @var \Slivki\Entity\Comment $comment */ 
        $entityManager = $this->getDoctrine()->getManager(); 
        $comment = $entityManager->getRepository(Comment::class)->find($commentID); 
        if (!$comment) { 
            return new Response(''); 
        } 
        if ($comment->getCreatedOn()->format('U') < strtotime('-24 hours') || $comment->getUserID() != $this->getUser()->getID()) { 
            return new Response(''); 
        } 
        $commentTypeID = $comment->getTypeID(); 
        $commentEntityID = $comment->getEntityID(); 
        $comment->setDeleted(true); 
        $comment->setChecked(false); 
        $comment->setRating(0); 
        $entityManager->flush(); 
        $this->resetCommentsCache($commentEntityID, $commentTypeID); 
        return new Response(''); 
    } 
 
    /** 
     * @Route("/mailing_seen_it_cheaper") 
     */ 
    public function seenCheaperAction(Request $request, Mailer $mailer){ 
        $email = $request->request->get("sender_email"); 
        $body = $request->request->get("body"); 
        $link = $request->request->get("link_to_sale"); 
        if($this->checkSeenCheaperForm($request)) { 
            $message = $mailer->createMessage(); 
            $message->setSubject("ОКО: Лучшее предложение") 
                ->setFrom("info@slivki.by", 'Slivki.by') 
                ->setTo('info@slivki.by') 
                ->setBody($this->renderView('Slivki/emails/seen_it_cheaper.html.twig', 
                    array('email' => $email, 'message' => $body, 'link' => $link)), 
                    'text/html'); 
            $mailer->send($message); 
 
            $result['status'] = "success"; 
        } else { 
            $result['status'] = "error"; 
        } 
 
        return new Response(json_encode($result)); 
    } 
 
    public function checkSeenCheaperForm(Request $request){ 
        $email = $request->request->get("sender_email"); 
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { 
            return false; 
        } 
 
        return true; 
    } 
 
    /** 
     * @Route("/ajax_get_map_placemarks_by_category") 
     */ 
    public function ajaxGetMapPlacemarksByCategory(Request $request) { 
        if (!$request->isXmlHttpRequest()) { 
            return $this->redirect("/"); 
        } 
        ini_set('memory_limit', '1024m'); 
        $categoryID = $request->request->get('categoryOID'); 
        $result = array(); 
        $offerRepository = $this->getOfferRepository(); 
        $offers = $offerRepository->getActiveOffersByCategoryID($categoryID); 
        foreach($offers as $offer) { 
            if (!$offer) { 
                continue; 
            } 
            $result = array_merge($result, $offerRepository->getOfferGeoLocations($offer)); 
        } 
 
        return new Response(json_encode($result)); 
    } 
 
    /** 
     * @Route("/mailing_campaign/{mailingCampaignID}") 
     */ 
    public function mailingCampaignAction($mailingCampaignID) { 
        $entityManager = $this->getDoctrine()->getManager(); 
        $mailingCampaignRepository = $entityManager->getRepository(MailingCampaign::class); 
        $mailingCampaign = $mailingCampaignRepository->find($mailingCampaignID); 
        if(!$mailingCampaign) { 
            return $this->redirect("/"); 
        } 
        $mailBody = $mailingCampaign->getMailBody(); 
 
        $template = $this->get('twig')->createTemplate($mailBody); 
        return new Response($template->render([])); 
    } 
 
    public function sendOfferMessageFormAction(Request $request) { 
        return $this->render(self::getMobileDevice($request) ? 
            'Slivki/mobile/offer/create_own_offer.html.twig' : 'Slivki/offers/send_offer_messsage.html.twig'); 
    } 
 
    /** 
     * @Route("/send_offer_message") 
     */ 
    public function sendOfferMessage(Request $request, Mailer $mailer) { 
        $offerProposal = new OfferProposal(); 
        $offerProposal->setType(OfferProposal::PROPOSAL_TYPE); 
        $offerProposal->setPhone($request->request->get('offerPhone')); 
        $offerProposal->setEmail($request->request->get('offerEmail')); 
        $offerProposal->setBuisnessName($request->request->get('offerBuisness')); 
        $offerProposal->setOfferConditions($request->request->get('termsOfPromotion')); 
 
        $subject = 'NEW: Разместить акцию'; 
        $messageText = '<b>Телефон:</b> ' . $request->request->get('offerPhone') . '<br>' 
            . '<b>E-mail:</b> ' . $request->request->get('offerEmail') . '<br>' 
            . '<b>Юр.лицо:</b> ' . $request->request->get('offerBuisness') . '<br>' 
            . '<b>Условия акции:</b> ' . $request->request->get('termsOfPromotion') . '<br>'; 
 
        $message = $mailer->createMessage(); 
        $message->setSubject($subject) 
            ->setFrom("info@slivki.by", 'Slivki.by') 
            ->setTo('info@slivki.by') 
            ->setBody( 
                $messageText, 
                'text/html' 
            ); 
 
        $mailer->send($message); 
 
        try { 
            $em = $this->getDoctrine()->getManager(); 
            $em->persist($offerProposal); 
            $em->flush(); 
        } catch (\Exception $e) { 
            return new Response('Ваше сообщение успешно отправлено'); 
        } 
        return new Response('Ваше сообщение успешно отправлено'); 
    } 
 
    /** 
     * @Route("/humorfm") 
     */ 
    public function humorFM(Request $request) { 
        if($this->getMobileDevice($request)){ 
            return $this->render('Slivki/mobile_humorfm.html.twig'); 
        } else{ 
            return $this->render('Slivki/humorfm.html.twig'); 
        } 
    } 
 
    /** 
     * @Route("/subscribe/mobile") 
     */ 
    public function subscribeForm(Request $request) { 
        if ($this->getMobileDevice($request) and (!$this->getUser() or !$this->getUser()->getAcceptNewsletter())) { 
            return $this->render('Slivki/subscribe_mobile.html.twig'); 
        } else { 
            return $this->redirect("/"); 
        } 
    } 
 
    /** 
     * @Route("/send-contact-form") 
     */ 
    public function sendContactForm(Request $request, Mailer $mailer) { 
        $data = [ 
            'email' => $request->request->get('email', ''), 
            'name' => $request->request->get('name', ''), 
            'body' => $request->request->get('body', '') 
        ]; 
 
        $error = ''; 
        if (!filter_var(trim($data['email'], FILTER_VALIDATE_EMAIL))) { 
            $error .= 'Пожалуйста, введите Ваш E-Mail.<br>'; 
        } 
        if (trim($data['body']) == '') { 
            $error .= 'Пожалуйста, введите текст сообщения.<br>'; 
        } 
        if ($error != '') { 
            return new Response($error); 
        } 
 
        $recipientEmail = (strpos($request->headers->get('referer'), '/beznal')) ? 'beznal@slivki.by' : 'info@slivki.by'; 
 
        $message = $mailer->createMessage(); 
        $message->setSubject("Сообщение") 
            ->setFrom("info@slivki.by", 'Slivki.by') 
            ->setTo($recipientEmail) 
            ->setBody( 
                $this->renderView( 
                    'Slivki/emails/contact_email.html.twig', 
                    $data 
                ), 
                'text/html' 
            ); 
        $mailer->send($message); 
 
        return new Response(); 
    } 
 
    /** 
     * @Route("/contact-mail-result") 
     */ 
    public function contactMailResult(Request $request) { 
        $data['lastComments'] = $this->getDoctrine()->getRepository(Comment::class)->findBy(["hidden" => false], ["createdOn" => "desc"], 3); 
 
        return $this->render(CommonUtil::isMobileDevice($request) ? 
            'Slivki/mobile/info_pages/contacts_form_result.html.twig' : 'Slivki/contact_mail_result.html.twig', $data); 
    } 
 
    /** 
     * @Route("/readability_sale_stat") 
     */ 
    public function readabilitySaleStat(Request $request) { 
        $id = $request->request->get('saleID'); 
        $timeOnPage = $request->request->get('timeOnPage'); 
        $percentOfScrolling = $request->request->get('percentOfScrolling'); 
        $userID = $this->getUser(); 
        if ($userID) { 
            $userID = $this->getUser()->getID(); 
        } 
        $pageHeight = $request->request->get('pageHeight'); 
        $readPx = $request->request->get('readPx'); 
        $date = new \DateTime(); 
        $readabilityStat = new ReadabilityStat(); 
        $readabilityStat->setEntityID($id); 
        $readabilityStat->setPercentOfScrolling($percentOfScrolling); 
        $readabilityStat->setTimeOnPage($timeOnPage); 
        $readabilityStat->setCreatedOn($date); 
        $readabilityStat->setUserID($userID); 
        $readabilityStat->setPageHeight($pageHeight); 
        $readabilityStat->setReadPx($readPx); 
 
        $em = $this->getDoctrine()->getManager(); 
        $em->persist($readabilityStat); 
        $em->flush($readabilityStat); 
 
        return new Response(''); 
    } 
 
    /** 
     * @Route("/jivo/hooks") 
     */ 
    public function jivoHookAction(Request $request) { 
        $data = json_decode($request->getContent(), true); 
        $logger = Logger::instance('JIVO HOOK'); 
        $entityManager = $this->getDoctrine()->getManager(); 
        $chatSession = null; 
        switch ($data['event_name']) { 
            case 'chat_accepted': 
                $chatSession = new ChatSession(); 
                $chatSession->setChatID($data['chat_id']); 
                $entityManager->persist($chatSession); 
                $chatSession->setOperatorEmail($data['agent']['email']); 
                $chatSession->setOperatorName($data['agent']['name']); 
                break; 
            case 'chat_finished': 
                $chatSession = $entityManager->getRepository(ChatSession::class)->findOneByChatID($data['chat_id']); 
                if (!$chatSession) { 
                    $logger->info('Chat ' . $data['chat_id'] . ' not found'); 
                    return new Response(); 
                } 
                $chatSession->setFinishedOn(new \DateTime()); 
                $chatHistory = ''; 
                foreach ($data['chat']['messages'] as $message) { 
                    $chatHistory .= date('d.m.Y', $message['timestamp']) . '<br>'; 
                    $chatHistory .= ($message['type'] == 'agent' ? 'Консультант' : 'Клиент') . '<br>'; 
                    $chatHistory .= $message['message'] . '<br><br>'; 
                } 
                $chatSession->setChatHistory($chatHistory); 
                break; 
            default: 
                return new Response(); 
        } 
        if (!$chatSession->getUser() && isset($data['user_token'])) { 
            $user = $entityManager->getRepository(User::class)->find($data['user_token']); 
            if ($user) { 
                $chatSession->setUser($user); 
            } else { 
                $logger->info('User ' . $data['user_token'] . ' not found'); 
            } 
        } 
        $entityManager->flush($chatSession); 
        if ($data['event_name'] == 'chat_accepted') { 
            $data['result'] = 'ok'; 
            $user = $chatSession->getUser(); 
            if ($user) { 
                $data['custom_data'][0]['title'] = 'Баланс'; 
                $data['custom_data'][0]['content'] = number_format($user->getFullBalance(), 2); 
                $data['contact_info']['name'] = $user->getFirstName(); 
                $data['contact_info']['phone'] = $user->getPhone(); 
                $data['contact_info']['email'] = $user->getEmail(); 
                $data['contact_info']['description'] = ''; 
            } 
            $response = new JsonResponse(); 
            $response->setCharset('UTF-8'); 
            $response->setEncodingOptions(JSON_UNESCAPED_UNICODE); 
            $response->setData($data); 
 
            return $response; 
        } 
 
        return new Response(); 
    } 
 
    /** 
     * @Route("/subscribe") 
     */ 
    public function subscribeAction(Request $request, Mailer $mailer) { 
        $email = mb_strtolower(trim($request->request->get('email'))); 
        $entityManager = $this->getDoctrine()->getManager(); 
        $user = $entityManager->getRepository(User::class)->loadUserByUsername($email, false); 
        if ($user && ($user->getAcceptNewsletter() || $user->getID() == $this->getUser()->getID())) { 
            if (!$user->getAcceptNewsletter()) { 
                $user->setAcceptNewsletter(true); 
                $entityManager->flush($user); 
            } 
            return new Response('0'); 
        } 
        $subscriber = $entityManager->getRepository(Subscriber::class)->findOneByEmail($email); 
        if ($subscriber && $subscriber->getConfirmationCode() == '') { 
            return new Response('0'); 
        } 
        if (!$subscriber) { 
            $validator = $this->get('validator'); 
            $emailConstraint = new Email(); 
            if ($email == '' || $validator->validate($email, $emailConstraint) != '') { 
                return new Response('1'); 
            } 
            $subscriber = new Subscriber(); 
            $subscriber->setEmail($email); 
            $entityManager->persist($subscriber); 
            $confirmationCode = md5($subscriber->getID() . $subscriber->getEmail()); 
            $subscriber->setConfirmationCode($confirmationCode); 
            $entityManager->flush($subscriber); 
        } 
        $messageBody = $this->get('twig')->render('Slivki/emails/confirm_email.html.twig', ['confirmationCode' => $subscriber->getConfirmationCode()]); 
        $message = $mailer->createMessage('Вы станете богаче!', $messageBody, 'text/html')->addTo($email)->addFrom('info@slivki.by'); 
        $mailer->send($message); 
 
        return new Response('2'); 
    } 
 
    /** 
     * @Route("/confirm/email/{confirmationCode}") 
     */ 
    public function confirmEmailAction($confirmationCode) { 
        $confirmationCode = pg_escape_string($confirmationCode); 
        $entityManager = $this->getDoctrine()->getManager(); 
        $sql = "select id, email from subscriber where md5(id::text || email) = '$confirmationCode'"; 
        $subscriber = $entityManager->getConnection()->executeQuery($sql)->fetch(); 
        if (!$subscriber) { 
            $this->addFlash(self::SHOW_INFO_DIALOG_PARAMETER, 'Пользователь не найден'); 
            return $this->redirect(CityRepository::$mainPageURL); 
        } 
        $user = $entityManager->getRepository(User::class)->loadUserByUsername($subscriber['email'], false); 
        if ($user) { 
            $user->setAcceptNewsletter(true); 
            $entityManager->flush($user); 
            $entityManager->getConnection()->executeQuery("delete from subscriber where id = $subscriber[id]"); 
        } else { 
            $entityManager->getConnection()->executeQuery("update subscriber set confirmation_code = '' where id = $subscriber[id]"); 
        } 
        $this->addFlash(self::SHOW_INFO_DIALOG_PARAMETER, 'Вы успешно подписаны на рассылку'); 
 
        return $this->redirect(CityRepository::$mainPageURL); 
    } 
 
    /** 
     * @Route("/oplata-promokoda-azs") 
     */ 
    public function getGasStationCode( 
        Request $request, 
        OfferCacheService $offerCacheService, 
        SubscriptionService $subscriptionService 
    ): Response { 
        $entityManager = $this->getDoctrine()->getManager(); 
        $user = $this->getUser(); 
        $userID = $user->getID(); 
        $offerID = Offer::PETROL_OFFER_ID; 
        $sql = "select id from offer_order where user_id = $userID and offer_id = $offerID order by id DESC limit 1"; 
        $orderID = $entityManager->getConnection()->executeQuery($sql)->fetchColumn(); 
        if ($orderID) { 
            $entityOption= $entityManager->getRepository(EntityOption::class)->findBy(['entityID' => $orderID]); 
            foreach ($entityOption as $val) { 
                switch ($val->getName()) { 
                    case "car_model": 
                        $data['carModel'] = $val->getValue(); 
                        break; 
                    case "car_number": 
                        $data['carNumber'] = $val->getValue(); 
                        break; 
                    case "phone_number": 
                        $data['phoneNumber'] = $val->getValue(); 
                        break; 
                } 
            } 
        } 
        $data['offerID'] = Offer::PETROL_OFFER_ID; 
        $offerRepository = $this->getOfferRepository(); 
        $offer = $offerCacheService->getOffer(Offer::PETROL_OFFER_ID); 
        $data['codeCost'] = $offerRepository->getCodeCost($offer); 
        $data['freeCodesCount'] = $offer->getFreeCodesCount(); 
        $data['freeCode'] = $offerRepository->isOfferFreeForUser($offer, $this->getUser()); 
        $data['useBalance'] =  $this->getUser()->getFullBalance() >= $offerRepository->getCodeCost($offer); 
        $userPhone = $user->getCurrentPhone(); 
        if ($userPhone) { 
            $data['phoneNumber'] = $userPhone->getPhoneNumber(); 
        } 
 
        $siteSettings = $this->getSiteSettings(); 
 
        $data['subscriptionPrice'] = $subscriptionService->isSubscriptionFinished($user) 
            ? $siteSettings->getSubscriptionPrice() 
            : $siteSettings->getSubscriptionFirstPayment(); 
 
        $data['subscriptionFullPrice'] = $siteSettings->getSubscriptionPrice(); 
 
        $subscription = $subscriptionService->getSubscription($user); 
        $data['allowedCodesCountBySubscription'] = $subscriptionService->isSubscriber($user) 
            ? $subscription->getNumberOfCodes() 
            : 0; 
 
        return $this->render(self::getMobileDevice($request) ? 'Slivki/mobile/payment/fuel.html.twig' 
            : 'Slivki/offers/get_code_for_gas_station.html.twig', $data); 
    } 
 
    /** 
     * @Route("/get_offer_comment_medias_list") 
     */ 
    public function getOfferCommentMediasList(Request $request) { 
        $entityID = $request->request->get('entityID'); 
        $entityType = $request->request->get('entityType'); 
        $offset = $request->request->get('offset'); 
        $limit = $request->request->get('limit'); 
        switch ($entityType) { 
            case 'category': 
                $data['commentAndMediaList'] = $this->getMediaRepository()->getOfferCommentMediaListByCategoryID($entityID, $offset, $limit); 
                break; 
            case 'offer': 
                $data['commentAndMediaList'] = $this->getMediaRepository()->getOfferCommentMediaListByOfferID($entityID, $offset, $limit); 
                break; 
            case 'all': 
                $data['commentAndMediaList'] = $this->getMediaRepository()->getOfferCommentMediaList($offset, $limit); 
                break; 
        } 
        return $this->render('Slivki/comments/media_block_list.html.twig', $data); 
    } 
 
    /** 
     * @Route("/get_top_comment_list") 
     */ 
    public function getTopCommentList(Request $request) { 
        $offset = $request->request->get('offset'); 
        $limit = $request->request->get('limit'); 
        $data['commentList'] = $this->getCommentRepository()->findBy(['checked' => true, 'rating' => 5], ['ID' => 'DESC'], $limit, $offset); 
        return $this->render('Slivki/category_dividers/comment_list.html.twig', $data); 
    } 
 
    /** @Route("/ne-nashli-chto-iskali") */ 
    public function lookingForAction(Request $request) { 
        return $this->render(self::getMobileDevice($request) ? 'Slivki/mobile/looking_for.html.twig' : 'Slivki/looking_for.html.twig'); 
    } 
 
    /** @Route("/send-looking-for") */ 
    public function sendLookingForAction(Request $request, Mailer $mailer) { 
        if (!$request->getMethod('POST')) { 
            return new Response(); 
        } 
        $message = $mailer->createMessage(); 
        $mailBody = "<b>Текст:</b><br>" 
            . $request->request->get('text') 
            . "<br><br><b>Email:</b><br>" 
            . $request->request->get('email') 
            . "<br><br><b>Телефон:</b><br>" 
            . $request->request->get('phone'); 
        $message->setSubject("Не нашли, что искали") 
            ->setFrom('info@slivki.by', 'Slivki.by') 
            ->setTo('info@slivki.by') 
            ->setBody($mailBody, 'text/html'); 
        $mailer->send($message); 
        return new Response(); 
    } 
 
    /** 
     * @Route("/get_sidebar") 
     */ 
    public function getSidebar(SlivkiTwigExtension $extension) { 
        return new Response($extension->getSidebar($this->get('twig'))); 
    } 
 
    /** 
     * @Route("/get_sidebar_item_list") 
     */ 
    public function getSidebarItemListContent( 
        Request $request, 
        SidebarCacheService $sidebarCacheService 
    ): Response { 
        $offset = $request->request->get('offset', 0); 
        $length = $request->request->get('length', 20); 
 
        $sidebarCached = $sidebarCacheService->getSidebarCached(); 
        if (null === $sidebarCached) { 
            return new Response(''); 
        } 
 
        $sidebarItemList = array_slice($sidebarCached->getItemList(), $offset, $length); 
        if (empty($sidebarItemList)) { 
            return new Response(''); 
        } 
 
        $sidebarContent = implode('', $sidebarItemList); 
 
        return new Response($sidebarContent); 
    } 
 
    /** 
     * @Route("/widget/{alias}") 
     * @Route("/widget/map/{alias}") 
     */ 
    public function widgetMapTireAction($alias) { 
        $softCache = new SoftCache('tire-widget'); 
        $result = $softCache->get($alias); 
        if ($result) { 
            return new Response($result); 
        } 
        $offers = null; 
        if ($alias == 'map_tire') { 
            $offers = $this->getOfferRepository()->getActiveOffersByCategoryIDNoCache(492); 
        } 
        if (!$offers) { 
            $seo = $this->getSeoRepository()->getByAlias('/' . $alias); 
            if (!$seo) { 
                return new Response(); 
            } 
            switch ($seo->getResourceURL()) { 
                case SeoRepository::RESOURCE_URL_OFFER_CATEGORY: 
                    $offers = $this->getOfferRepository()->getActiveOffersByCategoryID($seo->getEntityID()); 
                    break; 
                case SeoRepository::RESOURCE_URL_OFFER_DETAILS: 
                    $offer = $this->getOfferRepository()->getAnyWay($seo->getEntityID()); 
                    if ($offer) { 
                        $offers[] = $offer; 
                    } 
            } 
        } 
        if (!$offers || count($offers) == 0) { 
            return new Response(); 
        } 
        $result = []; 
        $i = 0; 
        $seoRepository = $this->getSeoRepository(Seo::class); 
        foreach($offers as $offer) { 
            if (!$offer) { 
                continue; 
            } 
            $geoLocations = $offer->getGeoLocations(); 
            foreach ($geoLocations as $geoLocation) { 
                $geoLocationInfos['markerAnnotation'] = $geoLocation->getDescription(); 
                $geoLocationInfos['latitude'] = $geoLocation->getLatitude(); 
                $geoLocationInfos['longitude'] = $geoLocation->getLongitude(); 
                $result[$i]['geoLocationInfos'][] = $geoLocationInfos; 
                $seo = $seoRepository->getByEntity('Slivki:Default:details', $offer->getID()); 
                $url = ''; 
                if ($seo) { 
                    $url = $seo->getMainAlias(); 
                } 
                $result[$i]['longMarkerDescription'] = "<div class=\"map-balloon-description--description\">" . $offer->getTitle() . "</div><div class=\"map-balloon-description--link\"><a target='_blank' href=\"" . $url . "\">Подробнее</a></div>"; 
                $i++; 
            } 
        } 
        $result = $this->renderView('Slivki/widget/map_tire.html.twig', ['placemarkList' => $result]); 
        $softCache->set($alias, $result, 12 * 60 * 60); 
 
        return new Response($result); 
    } 
 
 
    /** @Route("/get_sidebar_banner/{cityID}") */ 
    public function getSidebarBanner(BannerService $bannerService, $cityID) { 
        $cityID = (int)$cityID; 
        if (!$cityID) { 
            $cityID = City::DEFAULT_CITY_ID; 
        } 
        return new JsonResponse($bannerService->getSidebarBannerCached($cityID)); 
    } 
 
    /** @Route("/log/browser") */ 
    public function browserLogAction(Request $request) { 
        Logger::instance('BrowserLog')->info($request->request->get('message')); 
        return new Response(); 
    } 
 
    /** @Route("/category/filter/map/{categoryID}") */ 
    public function categoryMapFilterAction(Request $request, CacheService $cacheService, $categoryID) { 
        ini_set('memory_limit', '512M'); 
        $offerIDList = $request->request->get('offerList', []); 
        $teaserList = $cacheService->getTeaserList($offerIDList, CommonUtil::isMobileDevice($request)); 
        $result = ['html' => '', 'count' => 0]; 
        if ($teaserList) { 
            $data = ['offerList' => array_values($teaserList), 'offersInARow' => self::getMobileDevice($request) ? 2 : 3]; 
            $result = [ 
                'html' => $this->get('twig')->render('Slivki/offers/teasers.html.twig', $data), 
                'count' => count($teaserList) 
            ]; 
        } 
 
        return new JsonResponse($result); 
    } 
 
    /** @Route("/category/location-info/{categoryID}/{limit}/{offset}") */ 
    public function getLocationInfoAction($categoryID, $limit, $offset) { 
        $offerGeoLocationCache = new SoftCache(OfferRepository::CACHE_NAME_GEO_LOCATION_DATA); 
        $features = $offerGeoLocationCache->get($categoryID, []); 
        if (!$features) { 
            $features = []; 
        } 
        $features = array_slice($features, $offset, $limit); 
        if (empty($features)) { 
            return new Response(json_encode([])); 
        } 
        $getLocationData = ['type' => 'FeatureCollection', 'features' => $features]; 
        return new Response(json_encode($getLocationData)); 
    } 
 
    /** @Route("/top500") */ 
    public function topAction() { 
        $data['infoPage'] = new InfoPage(); 
        $data['text'] = $data['infoPage']->getContent(); 
 
        return $this->render('Slivki/pages/pages.html.twig', $data); 
    } 
 
    /** @Route("/category/get_supplier_address_tab/{directorID}") */ 
    public function getSupplierAddressTab(Request $request, $directorID) { 
        $entityManager = $this->getDoctrine()->getManager(); 
        $director = $entityManager->getRepository(Director::class)->find($directorID); 
        if (!$director) { 
            return new Response(); 
        } 
        $directorUniqueGeoLocations = []; 
        /** @var Offer $offer */ 
        foreach ($director->getOffers() as $offer) { 
            if (!$offer->isActive() || !$offer->isInActivePeriod()) { 
                continue; 
            } 
            /** @var GeoLocation $geoLocation */ 
            foreach ($offer->getGeoLocations() as $geoLocation) { 
                $isUniqueGeoLoation = true; 
                /** @var GeoLocation $uniqueGeoLocation */ 
                foreach ($directorUniqueGeoLocations as $uniqueGeoLocation) { 
                    if ($geoLocation->getCity() == $uniqueGeoLocation->getCity() && $geoLocation->getStreet() == $uniqueGeoLocation->getStreet() 
                        && $geoLocation->getHouse() == $uniqueGeoLocation->getHouse()) { 
                        $isUniqueGeoLoation = false; 
                        break; 
                    } 
                } 
                if ($isUniqueGeoLoation) { 
                    $directorUniqueGeoLocations[] = $geoLocation; 
                } 
            } 
        } 
        $data['geoLocationList'] = $directorUniqueGeoLocations; 
        return $this->render(self::getMobileDevice($request) ? 'Slivki/mobile/offer/supplier_address_tab.html.twig' 
            : 'Slivki/offers/supplier_address_tab.html.twig', $data); 
    } 
 
    /** @Route("/category/get_supplier_photoguide_tab/{directorID}") */ 
    public function getSupplierPhotoguideTab($directorID) { 
        $entityManager = $this->getDoctrine()->getManager(); 
        $director = $entityManager->getRepository(Director::class)->find($directorID); 
        if (!$director) { 
            return new Response(); 
        } 
        $teaserList = []; 
        $saleRepository = $this->getSaleRepository(); 
        /** @var Sale $sale */ 
        foreach ($director->getSales() as $sale) { 
            $saleCached = $saleRepository->findCached($sale->getID()); 
            $teaserList[] = $this->renderView('Slivki/sale/teaser.html.twig', ['sale' => $saleCached]); 
        } 
        $data['teaserList'] = $teaserList; 
        return $this->render('Slivki/offers/supplier_photoguide_tab.html.twig', $data); 
    } 
 
    /** @Route("/get-sorted-sidebar") */ 
    public function getSortedSidebar(Request $request) { 
        if (!$request->isMethod(Request::METHOD_POST)) { 
            return $this->redirectToRoute('homepage'); 
        } 
        $requestParameters = $request->request->all(); 
        if (!isset($requestParameters['sortBy'])) { 
            $requestParameters['sortBy'] = 'default'; 
        } 
        $categoryID = $requestParameters['categoryID'] ? $requestParameters['categoryID'] : null; 
        $coordinates = $requestParameters['coordinates'] != '' ? explode(',', $requestParameters['coordinates']) : null; 
        $sortedSaleIDList = $this->getSaleRepository()->getSaleIDSorted( 
            $requestParameters['sortBy'], $categoryID, $requestParameters['offset'], 
            $requestParameters['limit'], $coordinates); 
        $softCache = new SoftCache(SaleRepository::CACHE_NAME); 
        $sortedSaleList = $softCache->getMulti($sortedSaleIDList); 
        if (!is_array($sortedSaleList)) { 
            $sortedSaleList = []; 
        } 
        $sidebarContent = ''; 
        foreach ($sortedSaleList as $sale) { 
            if (!$sale) { 
                continue; 
            } 
            $sidebarContent .= $this->renderView('Slivki/sidebar/sale_teaser.html.twig', ['sale' => $sale]); 
        } 
        return new Response($sidebarContent); 
    } 
 
    /** @Route("/offer/food-extension-order") */ 
    public function offerFoodExtensionOrder(Request $request) { 
        if (!self::getMobileDevice($request)) { 
            return $this->redirectToRoute('homepage'); 
        } 
        return $this->render('Slivki/offers/food_extension/mobile/order.html.twig'); 
    } 
 
    /** @Route("/offer/food-extension-order-confirm") */ 
    public function offerFoodExtensionOrderConfirm(Request $request) { 
        if (!self::getMobileDevice($request)) { 
            return $this->redirectToRoute('homepage'); 
        } 
        return $this->render('Slivki/offers/food_extension/mobile/order_confirm.html.twig'); 
    } 
 
    /** @Route("/food-order/check/{orderID}") */ 
    public function checkFoodOrderStateActioin($orderID) { 
        $order = $this->getDoctrine()->getManager()->find(FoodOrder::class, $orderID); 
        if (!$order || $order->getStatus() != OfferOrder::STATUS_INIT) { 
            return new Response('paid'); 
        } 
        return new Response(); 
    } 
 
    /** @Route("/offer_supplier_image/{offerID}/{limit}/{offset}") */ 
    public function getOfferSupplierSliderAction(CacheService $cacheService, $offerID, $limit, $offset) { 
        $supplierOfferPhotoList = $cacheService->getMediaList($offerID, Media\OfferSupplierPhotoMedia::TYPE, $offset, $limit); 
        if (empty($supplierOfferPhotoList)) { 
            return new Response(''); 
        } 
        return $this->render('Slivki/comments/offer_supplier_photos.html.twig', 
            ['supplierOfferPhotoList' => $supplierOfferPhotoList]); 
    } 
 
    /** @Route("/location/confirm/test") */ 
    public function testLocationConfirmAction(Request $request) { 
        $request->request->set('showLocationConfirm', true); 
        return $this->indexAction($request); 
    } 
 
    /** 
     * @Route("/dreamland-registration/{code}") 
     */ 
    public function dreamlandPartnerAction(Request $request, $code) { 
        $entityManager = $this->getDoctrine()->getManager(); 
        $entityOptionRepository = $entityManager->getRepository(EntityOption::class); 
        $partnerCode = $entityOptionRepository->findOneBy([ 
            'entityTypeID' => null, 
            'name' => EntityOption::OPTION_DREAMLAND_PARTNER, 
            'value' => $code 
        ]); 
 
        if (!$partnerCode) { 
            return $this->redirect('/'); 
        } 
 
        if ($this->getUser()) { 
            $option = $entityOptionRepository->findOneBy([ 
                'entityTypeID' => EntityOption::USER_TYPE, 
                'name' => EntityOption::OPTION_DREAMLAND_PARTNER, 
                'entityID' => $this->getUser()->getID() 
            ]); 
            if (!$option) { 
                $option = new EntityOption(); 
                $option->setEntityTypeID(EntityOption::USER_TYPE); 
                $option->setEntityID($this->getUser()->getID()); 
                $option->setName(EntityOption::OPTION_DREAMLAND_PARTNER); 
                $option->setValue($code); 
                $entityManager->persist($option); 
                $entityManager->flush(); 
            } 
            return $this->redirect($entityManager->getRepository(Seo::class)->getOfferURL(Offer::DREAMLAND_OFFER_ID)->getMainAlias()); 
        } 
        $request->getSession()->set(EntityOption::OPTION_DREAMLAND_PARTNER, $code); 
        if (CommonUtil::isMobileDevice($request)) { 
            return $this->redirect($entityManager->getRepository(Seo::class)->getOfferURL(Offer::DREAMLAND_OFFER_ID)->getMainAlias()); 
        } 
        return $this->redirect($entityManager->getRepository(Seo::class)->getOfferURL(Offer::DREAMLAND_OFFER_ID)->getMainAlias()); 
    } 
    /** @Route("/newadformat") */ 
    public function newAdFormatActioin() { 
        return $this->render('Slivki/newadformat/index.html.twig'); 
    } 
 
    public function domainPlaceHolderAction(Request $request, $entityID) { 
        $offer = $this->getDoctrine()->getManager()->find(Offer::class, $entityID); 
        $director = $offer->getDirectors()->first(); 
        return $this->render(CommonUtil::isMobileDevice($request) ? 'Slivki/m/mobile/index.html.twig' : 'Slivki/m/index.html.twig', ['director' => $director]); 
    } 
 
    /** @Route("/email-test") */ 
    public function emailTestAction(Mailer $mailer) { 
        $message = $mailer->createMessage('test', 'test'); 
        $message->addTo('igoradv@gmail.com'); 
        $message->setFrom('info@slivki.by', 'Slivki.by'); 
        $mailer->send($message); 
        return new Response('sent'); 
    } 
 
    /** @Route("/prilozhenie-skidok", name = "mobileApp") */ 
    public function appPageAction(Request $request) { 
        $view = CommonUtil::isMobileDevice($request) ? 'Slivki/mobile/mobile_app.html.twig' : 'Slivki/mobile_app.html.twig'; 
        return $this->render($view); 
    } 
 
    /** @Route("/betera-advent") */ 
    public function beteraAdventCalendar(Request $request) { 
        return $this->render(self::getMobileDevice($request) ? 'Slivki/mobile/betera_advent/base.html.twig' : 'Slivki/betera_advent/base.html.twig'); 
    } 
    /** @Route("/profile/oplata-pay") */ 
    public function balanceWirtuallWallet(Request $request) { 
        return $this->render(self::getMobileDevice($request) ? 'Slivki/mobile/payment/slivki_pay/index.html.twig' : 'Slivki/payment/slivki_pay/index.html.twig'); 
    } 
 
    /** @Route("/virtual-wallet-pay") */ 
    public function balanceWirtuallWalletQuery(Request $request) { 
        return $this->render(self::getMobileDevice($request) ? 'Slivki/mobile/payment/slivki_pay/wirtualWallet.html.twig' : 'Slivki/payment/slivki_pay/wirtualWallet.html.twig'); 
    } 
 
    /** @Route("/callback") */ 
    public function callbackRequest(Request $request) { 
        return $this->render(self::getMobileDevice($request) ? 'Slivki/mobile/callback/index.html.twig' : 'Slivki/callback/index.html.twig'); 
    } 
 
    /** @Route("/profile/transfer-balance") */ 
    public function transferBalanceIsNotSlivkiPay(Request $request) { 
        return $this->render(self::getMobileDevice($request) ? 'Slivki/mobile/transfer_balance/index.html.twig' : 'Slivki/transfer_balance/index.html.twig'); 
    } 
 
    /** @Route("/bonuses") */ 
    public function getBonusesPartner(Request $request) { 
        return $this->render(self::getMobileDevice($request) ? 'Slivki/mobile/bonuses_partner/index.html.twig' : 'Slivki/bonuses_partner/index.html.twig'); 
    } 
 
    /** @Route("/error/{statusCode}") */ 
    public function errorAction($statusCode): Response { 
        return new Response('', $statusCode); 
    } 
}