<?php
namespace Newland\Toubiz\Poi\Neos\LinkHandler;

use Neos\ContentRepository\Domain\Model\NodeInterface;
use Neos\Flow\I18n\Locale;
use Neos\Flow\I18n\Translator;
use Neos\Flow\Mvc\Controller\ControllerContext;
use Newland\NeosCommon\LinkHandler\Domain\Model\Record;
use Newland\NeosCommon\LinkHandler\Handler\AbstractLinkHandler;
use Newland\Toubiz\Api\ObjectAdapter\Concern\ArticleConstants;
use Newland\Toubiz\Poi\Neos\Service\ArticleUrlService;
use Newland\Toubiz\Sync\Neos\Domain\Model\Article;
use Newland\Toubiz\Sync\Neos\Domain\Repository\ArticleRepository;
use Neos\Flow\Annotations as Flow;

class ArticleLinkHandler extends AbstractLinkHandler
{

    /**
     * @var ArticleRepository
     * @Flow\Inject()
     */
    protected $articleRepository;

    /**
     * @var ArticleUrlService
     * @Flow\Inject()
     */
    protected $articleUrlService;

    /**
     * @var Translator
     * @Flow\Inject()
     */
    protected $translator;

    /**
     * Generates a record link for the given record.
     *
     * @param Record $record
     * @param ControllerContext $controllerContext
     * @param NodeInterface $contextNode
     * @param bool $absolute
     * @return string|null
     */
    public function generateRecordLink(
        Record $record,
        ControllerContext $controllerContext,
        NodeInterface $contextNode,
        bool $absolute
    ): ?string {
        if (!($record instanceof ArticleRecord)) {
            $record = $this->findByIdentifier($record->getId());
        }

        if ($record === null) {
            return null;
        }

        return $this->articleUrlService->generateUrl(
            $record->getArticle(),
            $controllerContext,
            $contextNode
        );
    }

    /**
     * @param string $recordIdentifier
     * @return ArticleRecord|null
     */
    public function findByIdentifier(string $recordIdentifier): ?Record
    {
        $article = $this->articleRepository->withLanguage($this->language, function () use ($recordIdentifier) {
            return $this->articleRepository->findByIdentifier($recordIdentifier);
        });
        return $this->articleToRecord($article);
    }

    /**
     * @param string $searchTerm
     * @return Record[]
     */
    public function findBySearchTerm(string $searchTerm): array
    {
        $limit = 10;
        $articlesByType = [];
        foreach (ArticleConstants::ALL_TYPES as $type) {
            $articlesByType[$type] = $this->findBySearchTermAndType($searchTerm, $type, $limit);
        }

        $all = [ [] ];
        foreach ($this->limitTypedArticles($articlesByType, $limit) as $articles) {
            $all[] = $articles;
        }

        return array_merge(...$all);
    }


    private function findBySearchTermAndType(string $searchTerm, $type, int $limit): array
    {
        return $this->articleRepository->withLanguage($this->language, function () use ($searchTerm, $type, $limit) {
            $query = $this->articleRepository->createQueryBuilder('article');
            $query->where($query->expr()->andX(
                $query->expr()->eq('article.mainType', ':type'),
                $query->expr()->like('article.name', ':searchTerm')
            ));
            $query->setParameters([
                'searchTerm' => '%' . $searchTerm . '%',
                'type' => $type
            ]);
            $query->setMaxResults($limit);
            return array_map(
                [ $this, 'articleToRecord' ],
                $query->getQuery()->execute()
            );
        });
    }

    private function articleToRecord(Article $article = null): ?ArticleRecord
    {
        if ($article === null) {
            return null;
        }

        $record = new ArticleRecord($article);

        $type = $this->translator->translateById(
            'mainType.' . $article->getMainType(),
            [ ],
            null,
            $this->language ? new Locale($this->language) : null,
            'Models/Article',
            'Newland.Toubiz.Poi.Neos'
        );
        if ($type) {
            $record->setTitle(sprintf('%s [%s]', $record->getTitle(), $type));
        }

        return $record;
    }


    private function limitTypedArticles(array $articlesByType, int $limit)
    {
        $mixedArticles = [];
        foreach (ArticleConstants::ALL_TYPES as $type) {
            $mixedArticles[$type] = [];
        }

        $added = 0;
        for ($i = 0; $i < $limit; $i++) {
            foreach (ArticleConstants::ALL_TYPES as $type) {
                $record = $articlesByType[$type][$i] ?? null;
                if ($record) {
                    $mixedArticles[$type][] = $record;
                    $added++;
                }
                if ($added >= $limit) {
                    return $mixedArticles;
                }
            }
        }

        return $mixedArticles;
    }
}
