<?php

namespace App\Shared\Context;

use App\Clinic\Entity\Clinic;
use App\Doctor\Entity\Doctor;

/**
 * محیط کاری مؤثر یک درخواست: یا مطب شخصی یک پزشک، یا یک کلینیک.
 *
 * سرویس‌ها، آدرس‌ها و برنامهٔ نوبت‌دهی همگی به یکی از این دو تعلق دارند و هرگز بین
 * آن‌ها مشترک نمی‌شوند. type/id دقیقاً همان جفتی است که ServiceSection با
 * entity_type/entity_id ذخیره می‌کند.
 */
final class EntityContext
{
    public const TYPE_DOCTOR  = 'doctor';
    public const TYPE_CLINIC  = 'clinic';
    public const TYPE_UNKNOWN = 'unknown';

    private function __construct(
        public readonly string $type,
        public readonly ?int $id,
        public readonly ?Clinic $clinic = null,
        public readonly ?Doctor $doctor = null,
        /**
         * محیط از انتخابِ صریح کاربر آمده (clinic_uuid درخواست یا UserActiveContext)
         * و نه از fallbackِ نقش.
         *
         * جداسازیِ سختِ TenantFilter فقط روی محیط انتخاب‌شده اعمال می‌شود: کاربری
         * که هنوز محیطی برنگزیده در هیچ محیطی «نیست»، و قفل‌کردنش روی حدسِ نقش،
         * دادهٔ کلینیکی‌اش را که قانوناً حقش است پنهان می‌کند.
         */
        public readonly bool $chosen = false,
    ) {}

    public static function forDoctor(?Doctor $doctor): self
    {
        return new self(self::TYPE_DOCTOR, $doctor?->getId(), null, $doctor);
    }

    /** همان محیط، با علامتِ «کاربر خودش انتخابش کرده». */
    public function asChosen(): self
    {
        return new self($this->type, $this->id, $this->clinic, $this->doctor, true);
    }

    public static function forClinic(Clinic $clinic): self
    {
        return new self(self::TYPE_CLINIC, $clinic->getId(), $clinic);
    }

    /**
     * محیط یک رزرو: کلینیکِ داده‌شده، وگرنه مطب شخصی همان پزشک — همان قراردادی که
     * {@see \App\Appointment\Service\BookingContextResolver} با ?Clinic بیان می‌کند.
     */
    public static function forBooking(Doctor $doctor, ?Clinic $clinic): self
    {
        return $clinic !== null ? self::forClinic($clinic) : self::forDoctor($doctor);
    }

    public static function unknown(): self
    {
        return new self(self::TYPE_UNKNOWN, null);
    }

    public function isClinic(): bool { return $this->type === self::TYPE_CLINIC; }

    public function isResolved(): bool { return $this->id !== null; }

    /** @return array{0: string, 1: ?int} جفت (entity_type, entity_id) برای ServiceSection */
    public function toEntityPair(): array
    {
        return [$this->type, $this->id];
    }
}
