<?php

namespace App\Branch\Service;

use App\Auth\Entity\User;
use App\Doctor\Entity\DoctorAddress;
use App\Doctor\Repository\DoctorAddressRepository;
use App\Secretary\Security\SecretaryAccessChecker;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Context\EntityContextResolver;
use App\Shared\Exception\AppException;
use Symfony\Component\HttpFoundation\RequestStack;

/**
 * تک‌نقطهٔ تبدیل «uuid شعبه در request» به یک {@see DoctorAddress} از محیط جاری.
 *
 * لازم است چون doctor_addresses جفت (entity_type, entity_id) ندارد و در
 * GlobalTables::ENTITIES سراسری اعلام شده، پس TenantFilter رویش کار نمی‌کند:
 * findOneBy(['uuid' => …]) آدرس کلینیک دیگری را هم برمی‌گرداند. هر سه کنترلر این
 * دامنه از اینجا رد می‌شوند تا این بررسی جایی جا نیفتد.
 */
final class BranchResolver
{
    public function __construct(
        private readonly DoctorAddressRepository $addresses,
        private readonly EntityContextResolver   $contexts,
        private readonly SecretaryAccessChecker  $secretaryAccess,
        private readonly RequestStack            $requestStack,
    ) {}

    /**
     * جفت محیطِ این درخواست.
     *
     * منشی جدا حساب می‌شود چون EntityContextResolver او را مالک هیچ محیطی نمی‌شناسد —
     * همان استثنایی که ClinicServiceController::resolveEntity() هم دارد. مجوزش جداگانه
     * با denyUnlessGranted سنجیده می‌شود، اینجا فقط «کدام محیط» است.
     *
     * @return array{0: string, 1: int}
     * @throws AppException وقتی محیطی حل نشود
     */
    public function pair(User $user): array
    {
        [$type, $id] = $user->hasRole('ROLE_SECRETARY')
            ? $this->secretaryAccess->resolveOwnerEntity($user)
            : $this->contexts->resolve($user, $this->requestedClinicUuid())->toEntityPair();

        if ($id === null) {
            throw new AppException(ErrorCodes::ERR_FORBIDDEN_001, 'محیط کاری انتخاب نشده است', 403);
        }

        return [$type, (int) $id];
    }

    /**
     * ۴۰۴ می‌دهد نه ۴۰۳ — همان رفتار TenantFilter: وجودِ دادهٔ محیط دیگر لو نمی‌رود.
     *
     * @throws AppException
     */
    public function resolve(User $user, string $addressUuid): DoctorAddress
    {
        [$entityType, $entityId] = $this->pair($user);

        $address = $this->addresses->findByUuidForEntityPair($addressUuid, $entityType, $entityId);

        if ($address === null) {
            throw new AppException(ErrorCodes::ERR_NOT_FOUND_001, 'شعبه یافت نشد', 404);
        }

        return $address;
    }

    /** @return DoctorAddress[] شعبه‌های محیط جاری */
    public function listForContext(User $user): array
    {
        [$entityType, $entityId] = $this->pair($user);

        return $this->addresses->findForEntityPair($entityType, $entityId);
    }

    private function requestedClinicUuid(): ?string
    {
        $request = $this->requestStack->getCurrentRequest();
        if ($request === null) {
            return null;
        }

        $fromQuery = $request->query->get('clinic_uuid');
        if (is_string($fromQuery) && $fromQuery !== '') {
            return $fromQuery;
        }

        if (!in_array($request->getMethod(), ['POST', 'PATCH', 'PUT'], true)) {
            return null;
        }

        $body = json_decode($request->getContent(), true);

        return is_array($body) && is_string($body['clinic_uuid'] ?? null) && $body['clinic_uuid'] !== ''
            ? $body['clinic_uuid']
            : null;
    }
}
