<?php

namespace App\Shared\Tenant;

use App\Shared\Context\EntityContext;

/**
 * «آیا این موجودیت مالِ همین محیط است؟»
 *
 * برای جاهایی لازم است که یک uuid از خودِ درخواست می‌آید و موجودیت متناظرش را
 * TenantFilter پوشش نمی‌دهد — یا چون فرزند aggregate است (ServiceItem) یا چون
 * کاربر هنوز محیطی انتخاب نکرده و فیلتر خاموش است.
 *
 * موجودیت باید جفت محیطش را با getEntityType()/getEntityId() بدهد. فرزندان
 * aggregate می‌توانند این دو را به ریشه delegate کنند، همان‌طور که ServiceItem
 * به ServiceSection می‌دهد.
 */
final class TenantOwnershipChecker
{
    /** null یعنی موجودیتی پیدا نشد — همان‌قدر «مالِ این محیط نیست» که یک موجودیت بیگانه. */
    public function belongsTo(EntityContext $context, ?object $entity): bool
    {
        if (!$context->isResolved()) {
            return false;
        }

        [$type, $id] = $context->toEntityPair();

        return $this->belongsToPair($type, $id, $entity);
    }

    /**
     * برای فراخوانی‌هایی که جفت را از قبل به‌صورت اسکالر دارند و موجودیت محیط را در
     * دست ندارند. ساختن EntityContext از اسکالر عمداً ممکن نیست: چنین contextی
     * isClinic() درست می‌دهد ولی ->clinic تهی دارد و مصرف‌کننده را بی‌صدا می‌شکند.
     */
    public function belongsToPair(string $entityType, int $entityId, ?object $entity): bool
    {
        if ($entity === null) {
            return false;
        }

        if (!method_exists($entity, 'getEntityType') || !method_exists($entity, 'getEntityId')) {
            throw new \InvalidArgumentException(sprintf(
                '%s does not expose a tenant pair; add getEntityType()/getEntityId() or delegate them to its aggregate root.',
                $entity::class,
            ));
        }

        return $entity->getEntityType() === $entityType && $entity->getEntityId() === $entityId;
    }

    /**
     * یک بیگانه در فهرست، کل فهرست را رد می‌کند.
     *
     * @param iterable<object|null> $entities
     */
    public function allBelongTo(EntityContext $context, iterable $entities): bool
    {
        foreach ($entities as $entity) {
            if (!$this->belongsTo($context, $entity)) {
                return false;
            }
        }

        return true;
    }
}
