<?php

namespace AutoAdmin\Bundle\Content\Repository;

use AutoAdmin\Bundle\Content\DependencyInjection\Compiler\ContentRepositoryCompilerPass;
use AutoAdmin\Bundle\Content\Entity\Locale;
use AutoAdmin\Bundle\Content\Model\TranslatableInterface;
use Doctrine\ORM\Query\Parameter;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ManagerRegistry;
use Exception;
use RuntimeException;
use LogicException;
use ReflectionClass;

abstract class AbstractTranslatableContentRepository extends AbstractContentRepository
{
    public function __construct(ManagerRegistry $registry, ?string $entityClass = null)
    {
        $reflection = new ReflectionClass($entityClass);
        if (!$reflection->implementsInterface(TranslatableInterface::class)) {
            throw new LogicException(
                'Class "' . $entityClass . '" does not implement TranslatableInterface ' .
                'but is using a translatable repository "' . get_class($this) . '".'
            );
        }

        parent::__construct($registry, $entityClass);
    }

    protected function getLocaleValue(): ?Locale
    {
        if (empty($this->contextHelper->getCurrentContext())) {
            throw new RuntimeException(
                'SiteContext was not provided to repository, ' .
                'did you forgot to tag repository with "' . ContentRepositoryCompilerPass::CONTENT_REPOSITORY_TAG . '".'
            );
        }

        return $this->contextHelper->getLocale();
    }

    public function applyLocaleContext(QueryBuilder $qb): ?QueryBuilder
    {
        $params = $qb->getParameters();

        /** @var Parameter $param */
        foreach ($params as $param) {
            if ($param->getName() == 'aa_locale') {
                return null;
            }
        }

        $qb->andWhere('_tr.locale = :aa_locale')
            ->setParameter('aa_locale', $this->getLocaleValue());

        return $qb;
    }

    /**
     * @throws Exception
     */
    public function createContentQueryBuilder(string $alias = '_', string $indexBy = null, array $options = []): QueryBuilder
    {
        if (in_array($alias, ['_tr', '_l'])) {
            throw new Exception('Invalid repository alias used, "_tr" and "_l" are reserved.');
        }

        $qb = parent::createContentQueryBuilder($alias, $indexBy, $options);

        $qb
            ->join($alias . '.translations', '_tr')
            ->join('_tr.locale', '_l');

        if (!isset($options['disable_locale_filter']) || $options['disable_locale_filter'] !== true) {
            $this->applyLocaleContext($qb);
        }

        return $qb;
    }

    public function createAutoContentQueryBuilder(string $alias = '_', ?string $indexBy = null, array $options = []): QueryBuilder
    {
        if (in_array($alias, ['_tr', '_l'])) {
            throw new Exception('Invalid repository alias used, "_tr" and "_l" are reserved.');
        }

        return parent::createAutoContentQueryBuilder($alias, $indexBy, $options);
    }

    public function createContentFindByQueryBuilder(
        array $criteria,
        array $orderBy = null,
        ?int  $limit = null,
        ?int  $offset = null,
        array $groupBy = null
    ): QueryBuilder
    {
        $qb = parent::createContentFindByQueryBuilder($criteria, $orderBy, $limit, $offset, $groupBy);
        $qb->andWhere('_l.code = :locale')
            ->setParameter('locale', $this->contextHelper->getLocale()->getCode());

        return $qb;
    }

    public function findOneByIdAndLocale(string $id, ?string $locale)
    {
        return $this->autoContentFindOneBy(['_.id' => $id, '_l.code' => $locale]);
    }

    public function findOneByTranslatedSlug(string $slug, ?string $locale)
    {
        return $this->autoContentFindOneBy(['_tr.slug' => $slug, '_l.code' => $locale]);
    }
}
