<?php

namespace App\Clinic\Security;

use App\Auth\Entity\User;
use App\Doctor\Repository\DoctorRepository;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Context\EntityContextResolver;
use App\Shared\Exception\AppException;

/**
 * نقطهٔ واحدِ اعمالِ مجوزِ «پزشکِ عضوِ کلینیک» روی endpointهای چند-نقشه — قرینهٔ
 * SecretaryAccessChecker اما برای ClinicDoctorPermission.
 *
 * فقط پزشکی که در محیطِ فعالش (UserActiveContext.db_uuid = کلینیک) عضوِ همان کلینیک
 * است محدود می‌شود. بقیه — مالکِ کلینیک، ادمین، منشی، پزشکِ مطب شخصی، یا هر کاربری
 * که محیطش کلینیک نیست — دست‌نخورده عبور می‌کنند (این checker آن‌ها را محدود نمی‌کند).
 */
class ClinicDoctorAccessChecker
{
    public function __construct(
        private readonly EntityContextResolver         $contextResolver,
        private readonly DoctorRepository              $doctorRepo,
        private readonly ClinicDoctorPermissionChecker $permissions,
    ) {}

    /**
     * idِ کلینیکی که این پزشک در محیطِ فعالش عضوِ آن است — برای کنترلرهایی که
     * tenant را نقش‌محور حل می‌کنند و بدون این، پزشکِ عضو را به مطبِ شخصی‌اش می‌بردند
     * (نه دادهٔ کلینیک). null اگر محیط کلینیک نیست یا کاربر عضو نیست.
     */
    public function memberClinicId(User $user): ?int
    {
        $context = $this->contextResolver->resolve($user);
        if (!$context->isClinic()) {
            return null;
        }

        $doctor = $this->doctorRepo->findByUser($user);

        return $doctor !== null && $context->clinic->hasDoctor($doctor) ? $context->id : null;
    }

    /**
     * فقط پزشکِ عضوِ کلینیک را با ClinicDoctorPermission محدود کن؛ سایر کاربران true.
     */
    public function canOrNonMember(User $user, string $resource, string $action): bool
    {
        $context = $this->contextResolver->resolve($user);
        if (!$context->isClinic()) {
            // محیطِ مطب شخصی یا نامشخص → این checker مالِ او نیست.
            return true;
        }

        $clinic = $context->clinic;

        // مالکِ کلینیک هرگز با مجوزهای عضویت قفل نمی‌شود.
        if ($clinic->getUser()->getId() === $user->getId()) {
            return true;
        }

        $doctor = $this->doctorRepo->findByUser($user);
        if ($doctor === null || !$clinic->hasDoctor($doctor)) {
            // غیرعضو (مثلاً منشی) — این checker مالِ او نیست.
            return true;
        }

        return $this->permissions->can($user, $clinic, $resource, $action);
    }

    /** 403 اگر پزشکِ عضو مجاز نباشد؛ سایر کاربران بدون تغییر عبور می‌کنند. */
    public function denyUnlessGranted(User $user, string $resource, string $action): void
    {
        if (!$this->canOrNonMember($user, $resource, $action)) {
            throw new AppException(ErrorCodes::ERR_FORBIDDEN_001, null, 403);
        }
    }
}
