<?php

namespace App\Tests\Policy;

use App\Policy\Entity\Policy;
use App\Policy\Service\FieldRegistry;
use App\Policy\Service\OperatorRegistry;
use PHPUnit\Framework\TestCase;

/**
 * هر فیلدی که schema تبلیغ می‌کند، باید جایی در کد **واقعاً پر شود**.
 *
 * خطر مشخص است و بی‌صداست: قانونی که روی فیلدی شرط بگذارد که هیچ فراخوانی آن را
 * نمی‌فرستد، هرگز مطابقت نمی‌کند و هیچ خطایی هم نمی‌دهد. اپراتور قانون را می‌سازد،
 * فعالش می‌کند، و تا ابد فکر می‌کند دارد کار می‌کند.
 *
 * این تست عمداً ساختاری است نه رفتاری: پیمایشِ همهٔ مسیرهای واقعی برای هر فیلد،
 * دستگاه تستی می‌خواست بزرگ‌تر از خودِ موتور.
 */
class PolicyFieldCoverageTest extends TestCase
{
    public function testEveryAdvertisedFieldIsSuppliedSomewhereInTheCode(): void
    {
        $registry = new FieldRegistry(new OperatorRegistry());
        $fields   = [];

        foreach (Policy::CATEGORIES as $category) {
            foreach ($registry->forCategory($category) as $field) {
                $fields[$field][] = $category;
            }
        }

        $sources = $this->sourceFiles(dirname(__DIR__, 2) . '/src');
        $missing = [];

        foreach ($fields as $field => $categories) {
            $found = false;

            foreach ($sources as $file => $code) {
                // خودِ schema فقط نام را اعلام می‌کند؛ پر کردنش جای دیگری است.
                if (str_ends_with($file, 'PolicySchema.php') || str_ends_with($file, 'FieldRegistry.php')) {
                    continue;
                }

                if (str_contains($code, sprintf("'%s'", $field)) && str_contains($code, '=>')) {
                    $found = true;
                    break;
                }
            }

            if (!$found) {
                $missing[$field] = $categories;
            }
        }

        self::assertSame(
            [],
            $missing,
            'این فیلدها در schema هستند ولی هیچ‌جا در context پر نمی‌شوند: ' .
            json_encode($missing, JSON_UNESCAPED_UNICODE),
        );
    }

    /** @return array<string, string> مسیر => محتوا */
    private function sourceFiles(string $root): array
    {
        $files    = [];
        $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($root));

        foreach ($iterator as $file) {
            if ($file->isFile() && $file->getExtension() === 'php') {
                $files[$file->getPathname()] = (string) file_get_contents($file->getPathname());
            }
        }

        return $files;
    }
}
