<?php

/*
 * This file is part of the VITICORE2 package.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace CpCreation\VitiCore\Bridge\Symfony\Bundle\Twig\Components\News;

use CpCreation\VitiCore\Bridge\Symfony\Bundle\Twig\Components\Behavior\HasTemplateOverride;
use CpCreation\VitiCore\Media\Model\Image;
use CpCreation\VitiCore\News\Model\News;
use CpCreation\VitiCore\News\Model\Translation\NewsTranslation;
use CpCreation\VitiCore\News\Repository\NewsRepository;
use Doctrine\ORM\Query\Expr\Join;
use Knp\Component\Pager\Pagination\PaginationInterface;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Routing\Exception\ExceptionInterface as RoutingExceptionInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;

#[AsTwigComponent('news:SummaryList', template: '@components/news/SummaryList.html.twig')]
final class SummaryList
{
    use HasTemplateOverride;

    public int $limit = 6;
    public ?int $page = null;
    public string $pageParameterName = 'page';
    public int $excerptLength = 180;
    public ?string $detailRoute = 'news_category';
    public string $routeParameter = 'slug';
    public ?string $categorySlug = null;

    private ?PaginationInterface $pagination = null;

    public function __construct(
        private readonly NewsRepository $newsRepository,
        private readonly PaginatorInterface $paginator,
        private readonly RequestStack $requestStack,
        private readonly UrlGeneratorInterface $urlGenerator,
    ) {
    }

    public function getPagination(): PaginationInterface
    {
        if (null !== $this->pagination) {
            return $this->pagination;
        }

        $queryBuilder = $this->newsRepository
            ->createQueryBuilder('news')
            ->addSelect('translation')
            ->innerJoin('news.translations', 'translation', Join::WITH, 'translation.translatable = news.id')
            ->andWhere('news.unPublishedAt >= :date OR news.unPublishedAt IS NULL')
            ->andWhere('news.publishedAt <= :date OR news.publishedAt IS NULL')
            ->andWhere('news.disabled = FALSE')
            ->addOrderBy('news.eventAt', 'DESC')
            ->setParameter('date', new \DateTimeImmutable());

        if (null !== $this->categorySlug && '' !== mb_trim($this->categorySlug)) {
            $queryBuilder
                ->innerJoin('news.category', 'category')
                ->innerJoin('category.translations', 'categoryTranslation', Join::WITH, 'categoryTranslation.translatable = category.id')
                ->andWhere('categoryTranslation.slug = :categorySlug')
                ->setParameter('categorySlug', $this->categorySlug);
        }

        $this->pagination = $this->paginator->paginate(
            $queryBuilder,
            $this->getCurrentPage(),
            max(1, $this->limit),
            [
                PaginatorInterface::PAGE_PARAMETER_NAME => $this->pageParameterName,
            ]
        );

        return $this->pagination;
    }

    public function getPrimaryImage(News $news): ?Image
    {
        foreach ($news->getImages() as $newsImage) {
            $image = $newsImage->getImage();

            if (null !== $image && null !== $image->getPath()) {
                return $image;
            }
        }

        return null;
    }

    public function getUrl(News $news): ?string
    {
        if (null === $this->detailRoute || '' === mb_trim($this->detailRoute)) {
            return null;
        }

        $translation = $this->getTranslation($news);
        $slug = $translation?->getSlug();

        if (null === $slug || '' === mb_trim($slug)) {
            return null;
        }

        try {
            return $this->urlGenerator->generate($this->detailRoute, [
                $this->routeParameter => $slug,
            ]);
        } catch (RoutingExceptionInterface) {
            return null;
        }
    }

    public function getExcerpt(News $news): string
    {
        $translation = $this->getTranslation($news);

        if (null === $translation) {
            return '';
        }

        $text = html_entity_decode(strip_tags((string) $translation->getText()), \ENT_QUOTES | \ENT_HTML5, 'UTF-8');
        $text = mb_trim((string) preg_replace('/\s+/', ' ', $text));

        if ('' === $text || mb_strlen($text) <= $this->excerptLength) {
            return $text;
        }

        return mb_rtrim(mb_substr($text, 0, $this->excerptLength), " \t\n\r\0\x0B.,;:").'...';
    }

    private function getCurrentPage(): int
    {
        if (null !== $this->page) {
            return max(1, $this->page);
        }

        $request = $this->requestStack->getCurrentRequest();
        $page = $request?->query->getInt($this->pageParameterName, 1) ?? 1;

        return max(1, $page);
    }

    private function getTranslation(News $news): ?NewsTranslation
    {
        $translation = $news->translate($this->getLocale());

        if (!$translation instanceof NewsTranslation) {
            return null;
        }

        return $translation;
    }

    private function getLocale(): string
    {
        return $this->requestStack->getCurrentRequest()?->getLocale() ?? 'fr';
    }
}
