<?php

namespace Diamond\Application\Content;

use Core\Concrete\RequestAwareTrait;
use Core\Contracts\RequestAwareInterface;
use Diamond\Application\Content\Exceptions\PageNotFoundException;
use Diamond\Config\Main;
use Diamond\Domain\Content\ContentCollection;
use Diamond\Domain\ContentTag\ContentTagCollection;
use Diamond\Domain\Page\Contracts\PageEntityInterface;
use Diamond\Domain\PromoBlock\Contracts\PromoBlockEntityInterface;
use Diamond\Infrastructure\Models\ContentTag\ContentTag;
use Diamond\Infrastructure\Models\ContentTag\ContentTagQuery;
use Diamond\Infrastructure\Models\ContentTag\Map\ContentTagTableMap;
use Diamond\Infrastructure\Models\Domain\DomainQuery;
use Diamond\Infrastructure\Models\Page\PageQuery;
use Diamond\Repositories\Content\ContentCriteria;
use Diamond\Repositories\Content\ContentRepository;
use Diamond\Repositories\ContentCategory\ContentCategoryRepository;
use Diamond\Repositories\ContentTag\ContentTagRepository;
use Diamond\Repositories\PromoBlock\PromoBlockRepository;
use Diamond\Repositories\Site\SiteRepository;
use Diamond\Repositories\PromoBlock\PromoBlockCriteria;
use Diamond\Repositories\Site\SiteCriteria;
use Diamond\Application\Content\Exceptions\PromoBlockNotFoundException;
use Propel\Runtime\ActiveQuery\Criteria;
use Propel\Runtime\Map\TableMap;
use Symfony\Component\HttpFoundation\Request;

class ContentService implements RequestAwareInterface
{
    use RequestAwareTrait;

    public function __construct(private PromoBlockRepository      $promoBlockRepository,
                                private ContentRepository         $contentRepository,
                                private ContentTagRepository      $contentTagRepository,
                                private ContentCategoryRepository $contentCategoryRepository,
                                private SiteRepository            $siteRepository,
                                private Main                      $mainConf)
    {
    }

    public function getContentTagRepository(): ContentTagRepository
    {
        return $this->contentTagRepository;
    }

    /**
     * @return ContentRepository
     */
    public function getContentRepository(): ContentRepository
    {
        return $this->contentRepository;
    }

    /**
     * @return PromoBlockRepository
     */
    public function getPromoBlockRepository(): PromoBlockRepository
    {
        return $this->promoBlockRepository;
    }

    /**
     * @return ContentCategoryRepository
     */
    public function getContentCategoryRepository(): ContentCategoryRepository
    {
        return $this->contentCategoryRepository;
    }


    /**
     * @param string $code
     * @return ContentCollection
     * @throws \Repo\Concrete\Exceptions\Collection
     */
    public function getContentsByCategoryCode(string $code): ContentCollection
    {
        return $this->contentRepository->findByCriteria(
            ContentCriteria::create()
                ->setFilterByCategoryCode($code)
                ->setFilterByActive(true)
        );
    }

    /**
     * @param string $code
     * @return PromoBlockEntityInterface
     * @throws \Repo\Concrete\Exceptions\Collection
     */
    public function getPromoBlockByCode(string $code): PromoBlockEntityInterface
    {
        $sites = $this->siteRepository->findByCriteria(
            SiteCriteria::create()->setSearchByHost($this->request->getHost())->setLimit(1)
        );

        $site = $sites->current();

        $filter = PromoBlockCriteria::create()->setFilterByCode($code);

        if ($site) {
            $filter->setFilterBySiteId($site->getId());
        }

        $promoBlocks = $this->promoBlockRepository->findByCriteria($filter);


        if (!$promoBlock = $promoBlocks->current()) {
            throw new PromoBlockNotFoundException('promoblock not found by code ' . $code);
        }

        return $promoBlock;
    }


    public function getPromoBlockContentByCode(string $code): ?string
    {
        try {
            $block = $this->getPromoBlockByCode($code);
            return $block->getBody();
        } catch (PromoBlockNotFoundException $ex) {
            return null;
        }
    }


    /**
     * @param Request $request
     * @return PageEntityInterface
     */
    public function getPage(Request $request): PageEntityInterface
    {

        $page = null;

        //поиск офлайн странцы
        if ($this->mainConf->getOffline() === 'Y') {

            $page = PageQuery::create()
                ->filterBySystem('Y')
                ->filterByActive('Y')
                ->filterByCode('offline')
                ->findOne();

        } else {

            $withoutSuffix = str_replace($this->mainConf->getUrl_suffix(), '', $request->getPathInfo());
            //ищем по урл


            $domain = DomainQuery::create()
                ->filterByDomain(
                    sprintf("'%s' REGEXP `domain`", $this->request->getHost()),
                    \Propel\Runtime\ActiveQuery\Criteria::CUSTOM
                )->findOne();

            if ($domain) {


                //ищем по решулярке
                $page = PageQuery::create()
                    ->filterByActive('Y')
                    ->filterByDomainId($domain->getId())
                    ->filterBySeoRule('', Criteria::GREATER_THAN)
                    ->where('? REGEXP Page.SEO_RULE ', $withoutSuffix)
                    ->_or()
                    ->filterByUrl($request->getPathInfo())
                    ->_or()
                    ->where('Page.Url = ?', $withoutSuffix)
                    ->findOne();


                if (!$page) {

                    $page = PageQuery::create()
                        ->filterByActive('Y')
                        ->filterByDomainId($domain->getId())
                        ->filterByUrl($request->getPathInfo())
                        ->_or()
                        ->where('Page.Url = ?', $withoutSuffix)
                        ->findOne();

                }


            }


        }


        //если страница не найдена то 404page
        if (!$page) {
            throw new PageNotFoundException($request->getPathInfo());
        }

        return $page;
    }

    /**
     * Получение страницы для авторизации
     * @return PageEntityInterface|null
     */
    public function getPageAuth(): ?PageEntityInterface
    {
        return PageQuery::create()
            ->filterByActive('Y')
            ->filterByCode('auth')
            ->findOne();
    }

    /**Получение страницы с блокированным содержимым
     * @return PageEntityInterface|null
     */
    public function getPageAccess(): ?PageEntityInterface
    {
        return PageQuery::create()
            ->filterBySystem('Y')
            ->filterByActive('Y')
            ->filterByCode('access')
            ->findOne();
    }

    /**
     * @return PageEntityInterface|null
     */
    public function getPage404(): ?PageEntityInterface
    {
        return PageQuery::create()
            ->filterBySystem('Y')
            ->filterByActive('Y')
            ->filterByCode('page404')
            ->findOne();
    }

    /**
     * Получение списка тегов с кол-вом статей ,по разделу
     * @param string $code
     * @return ContentTagCollection
     */
    public function getUsedTagsByCategoryCode(string $code, bool $count = true): ContentTagCollection
    {

        $results = ContentTagQuery::create()
            ->select(['id', 'name', 'c'])
            ->addAsColumn('c', 'COUNT(1)')
            ->useContentTagRelQuery()
            ->select(['id', 'name'])
            ->useContentQuery()
            ->filterByActive('Y')
            ->useContentCategoryQuery()
            ->filterByCode($code)
            ->endUse()
            ->endUse()
            ->groupByContentTagId()
            ->endUse()
            ->find();

        $collection = new ContentTagCollection();

        foreach ($results as $row) {
            $tag = new ContentTag();
            $tag->setCountContent($row['c']);
            $collection->push($tag->fromArray($row, TableMap::TYPE_FIELDNAME));
        }

        return $collection;
    }
}