<?php

namespace App\Tests\Shared;

use PHPUnit\Framework\TestCase;

/**
 * فهرستِ بازبینی‌شدهٔ جست‌وجوهای «uuid از درخواست → موجودیت محیط‌دار».
 *
 * سه نشتی این فاز همگی همین شکل را داشتند و هیچ‌کدام در repository نبودند؛ در
 * کنترلر و سرویس بودند، جایی که uuid از بدنه/کوئری می‌آید و کسی محیط را نمی‌سنجد.
 * پس گارد هم باید همان‌جا باشد، نه روی DQLهای repository.
 *
 * قاعده: عدد هر فایل = تعداد findByUuid روی repositoryهای محیط‌دار در آن فایل، که
 * همگی بازبینی شده‌اند. اگر عدد عوض شود یعنی جست‌وجوی تازه‌ای اضافه شده و باید
 * ثابت شود محیطش بررسی می‌شود — با ownsRecord/ownsSection/ownedItem،
 * TenantOwnershipChecker، یا لنگرزدن به ریشه‌ای که خودش بررسی شده.
 *
 * @see \App\Shared\Tenant\TenantOwnershipChecker
 * @see docs/architecture/tenancy.md
 */
class TenantLookupInventoryTest extends TestCase
{
    /** repositoryهایی که موجودیت محیط‌دار یا فرزند aggregate برمی‌گردانند. */
    private const TENANT_REPOSITORY_VARIABLES = [
        'itemRepo', 'serviceItemRepo', 'sectionRepo', 'staffRepo',
        'noteRepo', 'attachmentRepo', 'callRepo', 'messageRepo', 'medicalRepo',
        'sessionRepo', 'sessionPaymentRepo', 'recordRepo',
        'tenantInsuranceRepo', 'tenantTagRepo', 'packageRepo', 'ruleRepo',
    ];

    /**
     * فایل => تعداد جست‌وجوی بازبینی‌شده.
     *
     * @var array<string, int>
     */
    private const REVIEWED = [
        // assertServicesMatchContext (مسیر ثبت نوبت) + خودِ گارد. جست‌وجوی مسیرِ اسلاتِ
        // سرویسی به ServiceBookingCalculator منتقل شد و همان‌جا شمرده می‌شود.
        'src/Appointment/Controller/AppointmentController.php'      => 2,
        // TenantOwnershipChecker::belongsToPair() روی هر uuid، پیش از هر بررسی دیگر
        'src/Appointment/Service/ServiceBookingCalculator.php'      => 2,
        // TenantOwnershipChecker روی بخش/سرویس/پرسنل و فهرست سرویس‌ها
        'src/Appointment/Controller/MyAppointmentsController.php'   => 1,
        // ownsSession / getEntityType روی صورتحساب، پرونده و مطالبه
        'src/Billing/Controller/BillingController.php'              => 3,
        // ownsSection ×۹ و مقایسهٔ مستقیم جفت ×۳ (پکیج، کالا، پرسنل)
        'src/ClinicService/Controller/ClinicServiceController.php'  => 10,
        'src/Discount/Controller/DiscountController.php'            => 1,
        // قرارداد بیمه با جفت، و سرویس با getSection()->getEntityType()
        'src/Insurance/Controller/InsuranceController.php'          => 5,
        // ownedItem() / ownedPackage()
        'src/Inventory/Controller/InventoryController.php'          => 2,
        'src/Inventory/Service/InventoryService.php'                => 1,
        // ownsRecord ×۳۳ و دو پرداختِ لنگرخورده به مراجعهٔ بررسی‌شده
        'src/Patient/Controller/PatientController.php'              => 35,
        // TenantOwnershipChecker در هر سه حلقه (قیمت‌گذاری، ساخت، ویرایش) + کالا
        'src/Patient/Service/PatientService.php'                    => 5,
        // ownsStaff()
        'src/Staff/Controller/StaffController.php'                  => 2,
    ];

    private function projectDir(): string
    {
        return dirname(__DIR__, 2);
    }

    /** @return array<string, int> */
    private function countLookups(): array
    {
        $pattern = '/->(' . implode('|', self::TENANT_REPOSITORY_VARIABLES) . ')->findByUuid\(/';
        $counts  = [];

        $files = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($this->projectDir() . '/src', \FilesystemIterator::SKIP_DOTS),
        );

        foreach ($files as $file) {
            if ($file->getExtension() !== 'php') {
                continue;
            }

            $found = preg_match_all($pattern, (string) file_get_contents($file->getPathname()));
            if ($found > 0) {
                $relative          = str_replace($this->projectDir() . '/', '', $file->getPathname());
                $counts[$relative] = $found;
            }
        }

        ksort($counts);

        return $counts;
    }

    public function testNoUnreviewedTenantLookupExists(): void
    {
        $actual   = $this->countLookups();
        $expected = self::REVIEWED;
        ksort($expected);

        self::assertSame($expected, $actual, <<<'MSG'
            جست‌وجوی «uuid از درخواست → موجودیت محیط‌دار» تغییر کرده.

            برای هر مورد تازه ثابت کن محیطش بررسی می‌شود (ownsRecord/ownsSection/
            ownedItem، TenantOwnershipChecker، یا لنگر به ریشهٔ بررسی‌شده) و بعد عدد
            همان فایل را در REVIEWED به‌روز کن. اگر عدد کم شده، فقط عدد را کم کن.
            MSG);
    }
}
