<?php

namespace App\Pricing\Entity;

use App\Doctor\Entity\DoctorAddress;
use App\Pricing\Repository\PriceListRepository;
use App\Shared\Tenant\TenantOwnedTrait;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Uid\Uuid;

/**
 * لیست قیمت با **بازهٔ تاریخ** — بند ۱۲ مستند.
 *
 * `Tariff` موجود فقط «سال» دارد، پس تغییر تعرفه از اول مهر قابل بیان نیست. این جدول
 * بازهٔ دقیق می‌گیرد و `Tariff` به‌عنوان لایهٔ پشتیبان سرِ جایش می‌ماند.
 *
 * `address` تهی‌پذیر است: `null` یعنی «همهٔ شعبه‌های این محیط». قیمت اختصاصی یک شعبه
 * از {@see \App\ClinicService\Entity\ServiceBranchOverride} می‌آید که بر این مقدم است.
 */
#[ORM\Entity(repositoryClass: PriceListRepository::class)]
#[ORM\Table(name: 'price_lists')]
#[ORM\Index(columns: ['entity_type', 'entity_id', 'active'], name: 'idx_price_list_tenant')]
#[ORM\Index(columns: ['starts_at', 'ends_at'], name: 'idx_price_list_range')]
class PriceList
{
    use TenantOwnedTrait;

    #[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: DoctorAddress::class)]
    #[ORM\JoinColumn(name: 'address_id', referencedColumnName: 'id', nullable: true, onDelete: 'CASCADE')]
    private ?DoctorAddress $address = null;

    #[ORM\Column(type: 'string', length: 150)]
    private string $name;

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

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

    /** تا فعال نشده هیچ اثری ندارد؛ ساختنِ پیش‌نویس نباید قیمت امروز را عوض کند. */
    #[ORM\Column(type: 'boolean', options: ['default' => false])]
    private bool $active = false;

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

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

    /** @var Collection<int, PriceListItem> */
    #[ORM\OneToMany(targetEntity: PriceListItem::class, mappedBy: 'priceList', cascade: ['persist', 'remove'], orphanRemoval: true)]
    private Collection $items;

    public function __construct(string $entityType, int $entityId, string $name, int $startsAt, int $endsAt)
    {
        if ($endsAt <= $startsAt) {
            throw new \InvalidArgumentException('Price list end must be after its start.');
        }

        $this->uuid      = Uuid::v4()->toRfc4122();
        $this->name      = $name;
        $this->startsAt  = $startsAt;
        $this->endsAt    = $endsAt;
        $this->createdAt = time();
        $this->updatedAt = time();
        $this->items     = new ArrayCollection();

        $this->assignTenantPair($entityType, $entityId);
    }

    public function getId(): ?int                 { return $this->id; }
    public function getUuid(): string             { return $this->uuid; }
    public function getAddress(): ?DoctorAddress  { return $this->address; }
    public function getName(): string             { return $this->name; }
    public function getStartsAt(): int            { return $this->startsAt; }
    public function getEndsAt(): int              { return $this->endsAt; }
    public function isActive(): bool              { return $this->active; }

    /** @return Collection<int, PriceListItem> */
    public function getItems(): Collection        { return $this->items; }

    public function setAddress(?DoctorAddress $v): self { $this->address = $v; $this->touch(); return $this; }
    public function setName(string $v): self            { $this->name = $v; $this->touch(); return $this; }
    public function setActive(bool $v): self            { $this->active = $v; $this->touch(); return $this; }

    public function covers(int $at): bool
    {
        return $this->active && $at >= $this->startsAt && $at < $this->endsAt;
    }

    public function overlaps(int $startsAt, int $endsAt): bool
    {
        return $startsAt < $this->endsAt && $endsAt > $this->startsAt;
    }

    public function touch(): void { $this->updatedAt = time(); }

    public function toArray(): array
    {
        return [
            'uuid'         => $this->uuid,
            'name'         => $this->name,
            'address_uuid' => $this->address?->getUuid(),
            'address_name' => $this->address?->getName(),
            'starts_at'    => $this->startsAt,
            'ends_at'      => $this->endsAt,
            'active'       => $this->active,
            'items'        => array_map(
                static fn (PriceListItem $i): array => $i->toArray(),
                $this->items->toArray(),
            ),
        ];
    }
}
