<?php

namespace App\Appointment\Availability\Entity;

use App\Appointment\Availability\Repository\ResourceOccupancyRepository;
use App\Resource\Entity\ClinicResource;
use App\Shared\Tenant\TenantOwnedTrait;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Uid\Uuid;

/**
 * «این منبع در این بازه گرفته است.»
 *
 * یک ردیف به‌ازای هر (بخشِ نوبت × منبع) — نه یکی به‌ازای کل نوبت. دقیقاً همین
 * ریزدانگی است که ظرفیت آزاد می‌کند: اپراتوری که در «انتظار اثر کرم» کاری ندارد،
 * ردیف اشغال هم ندارد و برای بیمار بعدی قابل استفاده است (بند ۷ مستند).
 *
 * بازهٔ ثبت‌شده **گسترده‌تر از بازهٔ بخش** است: زمان آماده‌سازی و تمیزکاری منبع هم
 * درونش می‌آید، چون منبع واقعاً در آن دقایق در دسترس نیست.
 */
#[ORM\Entity(repositoryClass: ResourceOccupancyRepository::class)]
#[ORM\Table(name: 'resource_occupancy')]
#[ORM\Index(columns: ['resource_id', 'starts_at', 'ends_at'], name: 'idx_occupancy_resource_range')]
#[ORM\Index(columns: ['entity_type', 'entity_id'], name: 'idx_occupancy_tenant')]
#[ORM\Index(columns: ['appointment_id'], name: 'idx_occupancy_appointment')]
class ResourceOccupancy
{
    use TenantOwnedTrait;

    /** نوبت قطعی. */
    public const STATUS_BOOKED = 'booked';

    /** رزرو موقت تا پایان مهلت — تسک ۰۷ آن را مصرف می‌کند. */
    public const STATUS_HOLD = 'hold';

    /**
     * لغو یا منقضی — ردیف **حذف فیزیکی نمی‌شود**.
     *
     * تاریخچهٔ اینکه چه منبعی کِی گرفته شده بود، ورودی گزارش بهره‌وری است و حذفش یعنی
     * پاک کردن همان چیزی که قرار است اندازه بگیریم.
     */
    public const STATUS_RELEASED = 'released';

    public const STATUSES = [self::STATUS_BOOKED, self::STATUS_HOLD, self::STATUS_RELEASED];

    /** وضعیت‌هایی که واقعاً منبع را می‌گیرند. */
    public const BLOCKING_STATUSES = [self::STATUS_BOOKED, self::STATUS_HOLD];

    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private ?int $id = null;

    #[ORM\Column(type: 'string', length: 36, unique: true)]
    private string $uuid;

    #[ORM\ManyToOne(targetEntity: ClinicResource::class)]
    #[ORM\JoinColumn(name: 'resource_id', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')]
    private ClinicResource $resource;

    /** تهی‌پذیر چون رزرو موقت هنوز نوبتی ندارد. */
    #[ORM\Column(name: 'appointment_id', type: 'integer', nullable: true)]
    private ?int $appointmentId = null;

    #[ORM\Column(name: 'starts_at', type: 'integer')]
    private int $startsAt;

    #[ORM\Column(name: 'ends_at', type: 'integer')]
    private int $endsAt;

    #[ORM\Column(type: 'string', length: 10, options: ['default' => self::STATUS_BOOKED])]
    private string $status = self::STATUS_BOOKED;

    #[ORM\Column(name: 'segment_name', type: 'string', length: 150, nullable: true)]
    private ?string $segmentName = null;

    /** رزرو موقتی که این اشغال از آن آمده؛ بعد از ثبت نهایی هم نگه داشته می‌شود. */
    #[ORM\Column(name: 'hold_id', type: 'integer', nullable: true)]
    private ?int $holdId = null;

    #[ORM\Column(name: 'created_at', type: 'integer')]
    private int $createdAt;

    public function __construct(ClinicResource $resource, int $startsAt, int $endsAt, string $status = self::STATUS_BOOKED)
    {
        if ($endsAt <= $startsAt) {
            throw new \InvalidArgumentException('Occupancy end must be after its start.');
        }

        if (!in_array($status, self::STATUSES, true)) {
            throw new \InvalidArgumentException(sprintf('Unknown occupancy status "%s".', $status));
        }

        $this->uuid      = Uuid::v4()->toRfc4122();
        $this->resource  = $resource;
        $this->startsAt  = $startsAt;
        $this->endsAt    = $endsAt;
        $this->status    = $status;
        $this->createdAt = time();

        $this->assignTenantPair($resource->getEntityType(), $resource->getEntityId());
    }

    public function getId(): ?int                 { return $this->id; }
    public function getUuid(): string             { return $this->uuid; }
    public function getResource(): ClinicResource { return $this->resource; }
    public function getAppointmentId(): ?int      { return $this->appointmentId; }
    public function getStartsAt(): int            { return $this->startsAt; }
    public function getEndsAt(): int              { return $this->endsAt; }
    public function getStatus(): string           { return $this->status; }
    public function getSegmentName(): ?string     { return $this->segmentName; }

    public function getHoldId(): ?int { return $this->holdId; }

    public function setAppointmentId(?int $v): self { $this->appointmentId = $v; return $this; }
    public function setHoldId(?int $v): self        { $this->holdId = $v; return $this; }

    public function markBooked(): self   { $this->status = self::STATUS_BOOKED; return $this; }
    public function markReleased(): self { $this->status = self::STATUS_RELEASED; return $this; }
    public function setSegmentName(?string $v): self { $this->segmentName = $v; return $this; }

    public function toArray(): array
    {
        return [
            'uuid'          => $this->uuid,
            'resource_uuid' => $this->resource->getUuid(),
            'resource_name' => $this->resource->getName(),
            'starts_at'     => $this->startsAt,
            'ends_at'       => $this->endsAt,
            'status'        => $this->status,
            'segment_name'  => $this->segmentName,
        ];
    }
}
