<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\MainBundle\Repository;

use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\UserGroup;
use Chill\MainBundle\Search\SearchApiQuery;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Symfony\Contracts\Translation\LocaleAwareInterface;

final class UserGroupRepository implements UserGroupRepositoryInterface, LocaleAwareInterface
{
    private readonly EntityRepository $repository;

    private string $locale;

    public function __construct(EntityManagerInterface $em)
    {
        $this->repository = $em->getRepository(UserGroup::class);
    }

    public function setLocale(string $locale): void
    {
        $this->locale = $locale;
    }

    public function getLocale(): string
    {
        return $this->locale;
    }

    public function find($id): ?UserGroup
    {
        return $this->repository->find($id);
    }

    public function findAll(): array
    {
        return $this->repository->findAll();
    }

    public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array
    {
        return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
    }

    public function findOneBy(array $criteria): ?UserGroup
    {
        return $this->repository->findOneBy($criteria);
    }

    public function getClassName(): string
    {
        return UserGroup::class;
    }

    public function provideSearchApiQuery(string $pattern, string $lang, string $selectKey = 'user-group'): SearchApiQuery
    {
        $query = new SearchApiQuery();
        $query
            ->setSelectKey($selectKey)
            ->setSelectJsonbMetadata("jsonb_build_object('id', ug.id)")
            ->setSelectPertinence('3 + SIMILARITY(LOWER(UNACCENT(?)), ug.label->>?) + CASE WHEN (EXISTS(SELECT 1 FROM unnest(string_to_array(label->>?, \' \')) AS t WHERE LOWER(t) LIKE \'%\' || LOWER(UNACCENT(?)) || \'%\')) THEN 100 ELSE 0 END', [$pattern, $lang, $lang, $pattern])
            ->setFromClause('chill_main_user_group AS ug')
            ->setWhereClauses('
                ug.active AND (
                SIMILARITY(LOWER(UNACCENT(?)), ug.label->>?) > 0.15
                OR LOWER(UNACCENT(ug.label->>?)) LIKE \'%\' || LOWER(UNACCENT(?)) || \'%\')
            ', [$pattern, $lang, $lang, $pattern]);

        return $query;
    }

    public function findByUser(User $user, bool $onlyActive = true, ?int $limit = null, ?int $offset = null): array
    {
        $qb = $this->buildQueryByUser($user, $onlyActive);

        if (null !== $limit) {
            $qb->setMaxResults($limit);
        }

        if (null !== $offset) {
            $qb->setFirstResult($offset);
        }

        // ordering thing
        $qb->addSelect('JSON_EXTRACT(ug.label, :lang) AS HIDDEN label_ordering')
            ->addOrderBy('label_ordering', 'ASC')
            ->setParameter('lang', $this->getLocale());

        return $qb->getQuery()->getResult();
    }

    public function countByUser(User $user, bool $onlyActive = true): int
    {
        $qb = $this->buildQueryByUser($user, $onlyActive);
        $qb->select('count(ug)');

        return $qb->getQuery()->getSingleScalarResult();
    }

    private function buildQueryByUser(User $user, bool $onlyActive): \Doctrine\ORM\QueryBuilder
    {
        $qb = $this->repository->createQueryBuilder('ug');
        $qb->where(
            $qb->expr()->orX(
                $qb->expr()->isMemberOf(':user', 'ug.users'),
                $qb->expr()->isMemberOf(':user', 'ug.adminUsers')
            )
        );

        $qb->setParameter('user', $user);

        if ($onlyActive) {
            $qb->andWhere(
                $qb->expr()->eq('ug.active', ':active')
            );
            $qb->setParameter('active', true);
        }

        return $qb;
    }
}
