<?php

declare(strict_types=1);

/*
 * 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\Doctrine\Repository;

use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\NonUniqueResultException;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ObjectRepository;
use Gedmo\Tree\Entity\Repository\NestedTreeRepository as BaseNestedTreeRepository;
use Symfony\Component\Uid\Uuid;

abstract class NestedTreeRepository extends BaseNestedTreeRepository implements ObjectRepository
{
    /**
     * @param class-string<object> $entityClass
     */
    public function __construct(private readonly EntityManagerInterface $em, string $entityClass, private readonly string $alias = 'o')
    {
        parent::__construct($em, $em->getClassMetadata($entityClass));
    }

    public function save(object $object): void
    {
        $this->em->persist($object);
        $this->em->flush();
    }

    public function remove(object $object): void
    {
        $this->em->remove($object);
        $this->em->flush();
    }

    public function persist(object $object): void
    {
        $this->em->persist($object);
    }

    public function flush(): void
    {
        $this->em->flush();
    }

    /**
     * @throws NonUniqueResultException
     */
    public function findOneById(int|Uuid $id): ?object
    {
        $qb = $this->createQueryBuilder($this->alias)
          ->where($this->alias.'.id = :id')
          ->setParameter('id', $id);

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

    /**
     * @return array<int, array<string, mixed>>
     */
    public function treeArray(?object $node = null): array
    {
        $qb = $this->getNodesHierarchyQueryBuilder($node);
        $aComponents = $qb->getQuery()->getResult(AbstractQuery::HYDRATE_ARRAY);

        return $this->buildTreeArray($aComponents);
    }

    /**
     * @return array<object>
     */
    public function tree(): array
    {
        return $this->createQueryBuilder('node')->getQuery()
          ->setHint(Query::HINT_INCLUDE_META_COLUMNS, true)
          ->getResult('tree');
    }

    public function treeQuery(?object $node = null): QueryBuilder
    {
        return $this->getNodesHierarchyQueryBuilder($node);
    }

    public function reorderForce(?object $node = null): QueryBuilder
    {
        return $this->getNodesHierarchyQueryBuilder($node);
    }
}
