<?php

namespace App\Patient\Security;

use App\Auth\Entity\User;
use App\Clinic\Entity\Clinic;
use App\Clinic\Security\ClinicDoctorPermissionChecker;
use App\Doctor\Repository\DoctorRepository;
use App\Secretary\Repository\DoctorSecretaryRepository;
use App\Shared\Context\EntityContext;
use App\Shared\Context\EntityContextResolver;

/**
 * «پرونده‌های کدام محیط را این کاربر می‌بیند، و درون آن محیط محدود به بیمارانِ
 * کدام پزشکان؟»
 *
 * پرسش اول را EntityContextResolver جواب می‌دهد؛ مسئولیت منحصربه‌فرد این کلاس
 * فقط پرسش دوم است. پروندهٔ کلینیکی per-بیمار است نه per-پزشک، پس محدودسازیِ
 * پزشکِ عضو و منشی نمی‌تواند روی خودِ محیط باشد.
 *
 * «پایان همکاری» منبع حقیقتِ جدا ندارد: ClinicDoctorPermission/DoctorSecretary با
 * active=false خودشان رد می‌کنند.
 */
class PatientRecordScopeResolver
{
    private const RESOURCE = 'patients';

    public function __construct(
        private readonly EntityContextResolver         $contextResolver,
        private readonly DoctorRepository              $doctorRepo,
        private readonly DoctorSecretaryRepository     $secretaryRepo,
        private readonly ClinicDoctorPermissionChecker $clinicPermissions,
    ) {}

    public function resolve(User $user): PatientRecordScope
    {
        $context = $this->contextResolver->resolve($user);

        if ($context->isClinic()) {
            return $this->restrictionInClinic($user, $context->clinic);
        }

        // پزشکی که هنوز رکورد Doctor ندارد، محیطش «doctor» با شناسهٔ تهی است —
        // assertPatientGate همان را ۴۰۳ می‌کند.
        return $context->type === EntityContext::TYPE_DOCTOR
            ? PatientRecordScope::forDoctor($context->id)
            : PatientRecordScope::unknown();
    }

    /**
     * مالک کلینیک همهٔ پرونده‌ها را می‌بیند؛ پزشکِ عضو فقط بیمارانِ خودش؛ منشی
     * فقط بیمارانِ پزشکانِ تخصیص‌یافته به او — عضویت در کلینیک به‌تنهایی یعنی
     * منشیِ یک پزشک پروندهٔ بیماران پزشک دیگر را ببیند.
     */
    private function restrictionInClinic(User $user, Clinic $clinic): PatientRecordScope
    {
        if ($clinic->getUser()->getId() === $user->getId()) {
            return PatientRecordScope::forClinic($clinic->getId());
        }

        if ($user->hasRole('ROLE_DOCTOR')) {
            $doctor = $this->doctorRepo->findByUser($user);
            if ($doctor === null) {
                return PatientRecordScope::unknown();
            }

            if (!$this->clinicPermissions->can($user, $clinic, self::RESOURCE, 'view')) {
                return PatientRecordScope::forDoctor($doctor->getId());
            }

            return PatientRecordScope::forClinicRestrictedToDoctors($clinic->getId(), [$doctor->getId()]);
        }

        if ($user->hasRole('ROLE_SECRETARY')) {
            $doctorIds = array_map(
                fn($d) => $d->getId(),
                $this->secretaryRepo->findDoctorsBySecretaryInClinic($user, $clinic),
            );

            return PatientRecordScope::forClinicRestrictedToDoctors($clinic->getId(), $doctorIds);
        }

        return PatientRecordScope::forClinic($clinic->getId());
    }
}
