<?php

namespace App\Tests\Shared;

use App\ClinicService\Entity\ServiceItem;
use App\ClinicService\Entity\ServiceSection;
use App\Doctor\Entity\Doctor;
use App\Patient\Entity\PatientRecord;
use App\Patient\Entity\PatientSession;
use App\Patient\Entity\SessionPayment;
use App\Shared\Tenant\TenantFilter;
use App\Tests\ApiTestCase;
use Doctrine\ORM\EntityManagerInterface;

/**
 * فرزندی که uuidش از خودِ درخواست می‌آید، جفت محیط **خودش** را دارد.
 *
 * فاز ۷ نتیجه گرفت فرزندان aggregate ستون لازم ندارند چون هر کوئری repository به
 * ریشه لنگر می‌زند. آن حرف دربارهٔ repositoryها درست بود و یک حالت را ندید:
 *
 *     $item = $this->serviceItemRepo->findByUuid($data['service_item_uuid']);
 *
 * جست‌وجو با uuid **خودش** یک کوئری بی‌لنگر است، و فیلتر وقتی ستونی نباشد کاری
 * نمی‌تواند بکند. هر سه نشتی فاز ۷ همین شکل را داشتند.
 *
 * این تست‌ها عمداً **هیچ گارد دستی صدا نمی‌زنند** — فقط خودِ فیلتر. اگر ستون‌ها
 * برداشته شوند، همگی قرمز می‌شوند.
 */
class RequestReachableChildTenantTest extends ApiTestCase
{
    private function em(): EntityManagerInterface
    {
        return static::getContainer()->get(EntityManagerInterface::class);
    }

    protected function tearDown(): void
    {
        $filters = $this->em()->getFilters();
        if ($filters->isEnabled(TenantFilter::NAME)) {
            $filters->disable(TenantFilter::NAME);
        }

        parent::tearDown();
    }

    private function enableFilterFor(string $type, int $id): void
    {
        $this->em()->getFilters()
            ->enable(TenantFilter::NAME)
            ->setParameter(TenantFilter::PARAM_TYPE, $type, 'string')
            ->setParameter(TenantFilter::PARAM_ID, $id, 'integer');
    }

    private function makeDoctor(): Doctor
    {
        $doctor = new Doctor($this->createUser(['ROLE_DOCTOR']), 'دکتر فرزند');
        $this->em->persist($doctor);
        $this->em->flush();

        return $doctor;
    }

    private function serviceItemOf(Doctor $doctor): ServiceItem
    {
        $section = new ServiceSection('doctor', $doctor->getId(), 'بخش');
        $this->em->persist($section);
        $item = new ServiceItem($section, 'سرویس', 100);
        $this->em->persist($item);
        $this->em->flush();

        return $item;
    }

    private function sessionOf(Doctor $doctor): PatientSession
    {
        $record = new PatientRecord('doctor', $doctor->getId(), $this->createUser(['ROLE_USER']), 'doctor', $doctor->getId());
        $this->em->persist($record);
        $session = new PatientSession($record);
        $this->em->persist($session);
        $this->em->flush();

        return $session;
    }

    /** جفت محیط از ریشه مشتق می‌شود، نه از ورودی سازنده — پس نمی‌تواند واگرا شود. */
    public function testTheChildInheritsItsRootsEnvironmentOnConstruction(): void
    {
        $doctor  = $this->makeDoctor();
        $item    = $this->serviceItemOf($doctor);
        $session = $this->sessionOf($doctor);

        self::assertSame('doctor', $item->getEntityType());
        self::assertSame($doctor->getId(), $item->getEntityId());
        self::assertSame($doctor->getId(), $session->getEntityId());
    }

    /** ✅ در محیط خودی، جست‌وجو با uuid موجودیت را می‌دهد. */
    public function testALookupByUuidSucceedsInsideItsOwnEnvironment(): void
    {
        $doctor = $this->makeDoctor();
        $uuid   = $this->serviceItemOf($doctor)->getUuid();

        $this->em->clear();
        $this->enableFilterFor('doctor', $doctor->getId());

        self::assertNotNull($this->em->getRepository(ServiceItem::class)->findOneBy(['uuid' => $uuid]));
    }

    /**
     * ❌ همان جست‌وجو در محیط دیگر تهی برمی‌گردد — **بدون هیچ گارد دستی**.
     * این همان نشتی مالی فاز ۷ است که حالا در سطح داده بسته شده.
     */
    public function testTheSameLookupReturnsNothingInAnotherEnvironment(): void
    {
        $doctor = $this->makeDoctor();
        $uuid   = $this->serviceItemOf($doctor)->getUuid();

        $this->em->clear();
        $this->enableFilterFor('doctor', $doctor->getId() + 1000);

        self::assertNull($this->em->getRepository(ServiceItem::class)->findOneBy(['uuid' => $uuid]));
    }

    /** ❌ همین قاعده برای مراجعه — که ۷ نقطهٔ جست‌وجو با uuid دارد. */
    public function testASessionOfAnotherEnvironmentIsInvisible(): void
    {
        $doctor = $this->makeDoctor();
        $uuid   = $this->sessionOf($doctor)->getUuid();

        $this->em->clear();
        $this->enableFilterFor('doctor', $doctor->getId() + 1000);

        self::assertNull($this->em->getRepository(PatientSession::class)->findOneBy(['uuid' => $uuid]));
    }

    /** ⚠️ مرزی: نوهٔ ریشه (پرداختِ مراجعه) هم محیطش را از همان زنجیره می‌گیرد. */
    public function testAGrandchildTakesTheEnvironmentOfTheChainToo(): void
    {
        $doctor  = $this->makeDoctor();
        $session  = $this->sessionOf($doctor);
        $payment = new SessionPayment($session, 'cash', 50_000);
        $this->em->persist($payment);
        $this->em->flush();
        $uuid = $payment->getUuid();

        self::assertSame($doctor->getId(), $payment->getEntityId());

        $this->em->clear();
        $this->enableFilterFor('doctor', $doctor->getId() + 1000);

        self::assertNull($this->em->getRepository(SessionPayment::class)->findOneBy(['uuid' => $uuid]));
    }
}
