<?php

/*
 * This file is part of the Serenity 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 Serenity\CoreBundle\Shared\Form\Loader;

use Doctrine\ORM\EntityRepository;
use Serenity\CoreBundle\Shared\Common\IdentifierInterface;
use Symfony\Bridge\Doctrine\Form\ChoiceList\EntityLoaderInterface;

/**
 * @template T of IdentifierInterface
 */
final readonly class PaginatedChoiceLoader implements EntityLoaderInterface
{
    /**
     * @param EntityRepository<T> $repository
     * @param list<T>             $selecteds
     */
    public function __construct(
        private EntityRepository $repository,
        private int $limit,
        private int $offset,
        private array $selecteds = [],
    ) {
    }

    /**
     * @return list<T>
     */
    public function getPaginatedEntities(): array
    {
        /** @var list<T> $results */
        $results = $this->repository->createQueryBuilder('p')
            ->setFirstResult($this->offset)
            ->setMaxResults($this->limit)
            ->getQuery()
            ->getResult();

        return $results;
    }

    /**
     * @return list<T>
     */
    public function getEntities(): array
    {
        $paginated = $this->getPaginatedEntities();

        /** @var array<string, T> $merged */
        $merged = [];
        /** @var array<string, true> $existingIds */
        $existingIds = [];

        foreach ($paginated as $entity) {
            $id = (string) $entity->getId();
            $merged[$id] = $entity;
            $existingIds[$id] = true;
        }

        foreach ($this->selecteds as $entity) {
            $id = (string) $entity->getId();
            if (!isset($existingIds[$id])) {
                $merged[$id] = $entity;
            }
        }

        return array_values($merged);
    }

    /**
     * @param list<string|int> $values
     *
     * @return list<T>
     */
    public function getEntitiesByIds(string $identifier, array $values): array
    {
        /** @var list<T> $results */
        $results = $this->repository->createQueryBuilder('e')
            ->where(\sprintf('e.%s IN (:ids)', $identifier))
            ->setParameter('ids', $values)
            ->getQuery()
            ->getResult();

        return $results;
    }
}
