<?php

namespace App\Tests\Rating;

use App\Doctor\Entity\Doctor;
use App\Rating\Entity\Comment;
use App\Rating\Entity\Like;
use App\Tests\ApiTestCase;
use Doctrine\ORM\PersistentCollection;

/**
 * The public comment list fetch-joins likes/replies/authors (two passes) to
 * avoid lazy loading per comment (N+1). This guards the refactor's correctness:
 * the serialized tree — like counts, approved replies, author names — must be
 * unchanged.
 */
class CommentListNPlusOneTest extends ApiTestCase
{
    public function testTreeIsSerializedCorrectly(): void
    {
        $doctor = new Doctor($this->createUser(['ROLE_DOCTOR']), 'دکتر تست');
        $this->em->persist($doctor);

        $root = (new Comment($this->createUser(), $doctor, 'نظر ریشه'))->approve();
        $this->em->persist($root);
        // two likes + one dislike → like_count 2, dislike_count 1
        $this->em->persist(new Like($this->createUser(), $root, 1));
        $this->em->persist(new Like($this->createUser(), $root, 1));
        $this->em->persist(new Like($this->createUser(), $root, -1));

        $reply = (new Comment($this->createUser(), $doctor, 'پاسخ تأییدشده', $root))->approve();
        $this->em->persist($reply);
        // a pending reply must NOT appear
        $this->em->persist(new Comment($this->createUser(), $doctor, 'پاسخ در انتظار', $root));
        $this->em->flush();

        $this->em->clear();

        $body = $this->jsonGet('/api/v1/comments/' . $doctor->getUuid());

        $roots = $body['data']['data'];
        $this->assertCount(1, $roots);

        $r = $roots[0];
        $this->assertSame('نظر ریشه', $r['comment']);
        $this->assertSame(2, $r['like_status']['like_count']);
        $this->assertSame(1, $r['like_status']['dislike_count']);
        $this->assertCount(1, $r['replies']); // only the approved reply
        $this->assertSame('پاسخ تأییدشده', $r['replies'][0]['comment']);
    }

    private function jsonGet(string $uri): array
    {
        $this->client->request('GET', $uri);

        return json_decode($this->client->getResponse()->getContent(), true) ?? [];
    }

    /**
     * True N+1 regression: after the repository loads the roots, their likes and
     * replies collections must already be initialised (fetch-joined). Without the
     * fetch-join they are lazy and would each trigger a query during serialization.
     */
    public function testRepositoryFetchJoinsCollections(): void
    {
        $doctor = new Doctor($this->createUser(['ROLE_DOCTOR']), 'دکتر تست');
        $this->em->persist($doctor);
        $root = (new Comment($this->createUser(), $doctor, 'نظر'))->approve();
        $this->em->persist($root);
        $this->em->persist(new Like($this->createUser(), $root, 1));
        $this->em->persist((new Comment($this->createUser(), $doctor, 'پاسخ', $root))->approve());
        $this->em->flush();
        $doctorId = $doctor->getId();

        // fresh load — collections must come back initialised, not lazy proxies
        $this->em->clear();
        $doctor = $this->em->getRepository(Doctor::class)->find($doctorId);
        $roots  = $this->em->getRepository(Comment::class)->findApprovedRootsByDoctor($doctor);

        $this->assertNotEmpty($roots);
        foreach ($roots as $r) {
            $likes   = $r->getLikes();
            $replies = $r->getReplies();
            $this->assertInstanceOf(PersistentCollection::class, $likes);
            $this->assertInstanceOf(PersistentCollection::class, $replies);
            $this->assertTrue($likes->isInitialized(), 'likes collection was not fetch-joined');
            $this->assertTrue($replies->isInitialized(), 'replies collection was not fetch-joined');
        }
    }
}
