<?php

namespace App\Resource\Service;

use App\Auth\Entity\User;
use App\Branch\Service\BranchResolver;
use App\Doctor\Entity\DoctorAddress;
use App\Resource\Entity\ClinicResource;
use App\Resource\Entity\ResourcePool;
use App\Resource\Entity\ResourceType;
use App\Resource\Entity\Skill;
use App\Resource\Repository\ClinicResourceRepository;
use App\Resource\Repository\ResourcePoolRepository;
use App\Resource\Repository\ResourceTypeRepository;
use App\Resource\Repository\SkillRepository;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Exception\AppException;
use App\Shared\Tenant\TenantOwnershipChecker;

/**
 * تبدیل uuidهای درخواست به موجودیت‌های **محیط جاری**.
 *
 * مالکیت صریح سنجیده می‌شود و به `TenantFilter` تکیه نمی‌کنیم: جداسازی سختِ فیلتر فقط
 * روی محیطِ «انتخاب‌شده» اعمال می‌شود، پس کاربری که هنوز محیطی برنگزیده، دادهٔ محیط
 * دیگر را می‌دید. همان درسی که در تسک ۰۱ با `RoomCrudTest::testForeignRoomIsNotFound`
 * گرفته شد ({@see docs/architecture/tenancy.md}).
 *
 * همه‌جا ۴۰۴ می‌دهد نه ۴۰۳ — وجود دادهٔ محیط بیگانه لو نمی‌رود.
 */
final class ResourceContext
{
    public function __construct(
        private readonly BranchResolver           $branches,
        private readonly ResourceTypeRepository   $types,
        private readonly ClinicResourceRepository $resources,
        private readonly SkillRepository          $skills,
        private readonly ResourcePoolRepository   $pools,
        private readonly TenantOwnershipChecker   $ownership,
    ) {}

    /** @return array{0: string, 1: int} */
    public function pair(User $user): array
    {
        return $this->branches->pair($user);
    }

    public function address(User $user, string $addressUuid): DoctorAddress
    {
        return $this->branches->resolve($user, $addressUuid);
    }

    public function type(User $user, string $uuid): ResourceType
    {
        return $this->owned($user, $this->types->findByUuid($uuid), 'نوع منبع یافت نشد');
    }

    public function resource(User $user, string $uuid): ClinicResource
    {
        return $this->owned($user, $this->resources->findByUuid($uuid), 'منبع یافت نشد');
    }

    public function skill(User $user, string $uuid): Skill
    {
        return $this->owned($user, $this->skills->findByUuid($uuid), 'مهارت یافت نشد');
    }

    public function pool(User $user, string $uuid): ResourcePool
    {
        return $this->owned($user, $this->pools->findByUuid($uuid), 'استخر منابع یافت نشد');
    }

    /**
     * @template T of object
     * @param T|null $entity
     * @return T
     */
    private function owned(User $user, ?object $entity, string $message): object
    {
        [$entityType, $entityId] = $this->pair($user);

        if ($entity === null || !$this->ownership->belongsToPair($entityType, $entityId, $entity)) {
            throw new AppException(ErrorCodes::ERR_NOT_FOUND_001, $message, 404);
        }

        return $entity;
    }
}
