<?php

namespace App\Rating\Repository;

use App\Doctor\Entity\Doctor;
use App\Rating\Entity\Comment;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\Tools\Pagination\Paginator;
use Doctrine\Persistence\ManagerRegistry;

class CommentRepository extends ServiceEntityRepository
{
    public function __construct(ManagerRegistry $registry) { parent::__construct($registry, Comment::class); }

    public function findByUuid(string $uuid): ?Comment { return $this->findOneBy(['uuid' => $uuid]); }

    /** @return Comment[] */
    public function findApprovedByDoctor(Doctor $doctor): array
    {
        return $this->findBy(['doctor' => $doctor, 'status' => Comment::STATUS_APPROVED], ['createdAt' => 'DESC']);
    }

    public function countApprovedRootsByDoctor(Doctor $doctor): int
    {
        return (int) $this->createQueryBuilder('c')
            ->select('COUNT(c.id)')
            ->where('c.doctor = :doctor')
            ->andWhere('c.status = :status')
            ->andWhere('c.parent IS NULL')
            ->setParameter('doctor', $doctor)
            ->setParameter('status', Comment::STATUS_APPROVED)
            ->getQuery()
            ->getSingleScalarResult();
    }

    /** @return Comment[] approved root comments (no parent) for a doctor */
    public function findApprovedRootsByDoctor(Doctor $doctor, int $limit = 50, int $offset = 0): array
    {
        // Two fetch-join passes (not the product, to avoid a likes×replies
        // cartesian) hydrate everything toArray() touches, so serialization
        // triggers no per-comment lazy loads (N+1): pass 1 = roots + author +
        // likes, pass 2 = replies + their author + likes, merged into the same
        // managed root entities. Paginator(fetchJoinCollection) keeps the LIMIT
        // paginating by root comment, not by joined like rows.
        $qb = $this->createQueryBuilder('c')
            ->addSelect('u', 'l')
            ->leftJoin('c.user', 'u')
            ->leftJoin('c.likes', 'l')
            ->where('c.doctor = :doctor')
            ->andWhere('c.status = :status')
            ->andWhere('c.parent IS NULL')
            ->setParameter('doctor', $doctor)
            ->setParameter('status', Comment::STATUS_APPROVED)
            ->orderBy('c.createdAt', 'DESC')
            ->setFirstResult($offset)
            ->setMaxResults($limit);

        $roots = iterator_to_array(new Paginator($qb, fetchJoinCollection: true));

        if ($roots !== []) {
            // Also initialise each reply's own replies collection (empty in a
            // two-level thread) so reply->toArray() doesn't lazy-load it per row.
            $this->createQueryBuilder('c')
                ->addSelect('r', 'ru', 'rl', 'rr')
                ->leftJoin('c.replies', 'r')
                ->leftJoin('r.user', 'ru')
                ->leftJoin('r.likes', 'rl')
                ->leftJoin('r.replies', 'rr')
                ->where('c IN (:roots)')
                ->setParameter('roots', $roots)
                ->getQuery()
                ->getResult();
        }

        return $roots;
    }

    public function countPending(): int
    {
        return $this->count(['status' => Comment::STATUS_PENDING]);
    }

    /** @return Comment[] */
    public function findPending(int $limit = 50, int $offset = 0): array
    {
        return $this->findBy(['status' => Comment::STATUS_PENDING], ['createdAt' => 'ASC'], $limit, $offset);
    }

    public function save(Comment $e, bool $flush = true): void { $this->getEntityManager()->persist($e); if ($flush) $this->getEntityManager()->flush(); }
    public function remove(Comment $e, bool $flush = true): void { $this->getEntityManager()->remove($e); if ($flush) $this->getEntityManager()->flush(); }
}
