<?php

namespace App\ClinicService\Entity;

use App\ClinicService\Repository\ItemGroupRepository;
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;

/**
 * گروه انتخابِ آیتم درون یک سرویس — «نواحی بدن»، «سطح انرژی».
 *
 * بند ۵ مستند: این قوانین **نباید** به موتور قوانین سپرده شوند. «حتماً یک سطح انرژی،
 * فقط یکی» یک عدد است (`min=1, max=1`)، نه یک قانون؛ سپردنش به موتور یعنی هر سرویس
 * چند قانون و هیچ‌کس نمی‌فهمد چرا انتخابش رد شد.
 */
#[ORM\Entity(repositoryClass: ItemGroupRepository::class)]
#[ORM\Table(name: 'service_item_groups')]
#[ORM\Index(columns: ['entity_type', 'entity_id'], name: 'idx_item_group_tenant')]
#[ORM\Index(columns: ['service_id', 'sort_order'], name: 'idx_item_group_service')]
class ItemGroup
{
    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: ServiceItem::class)]
    #[ORM\JoinColumn(name: 'service_id', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')]
    private ServiceItem $service;

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

    #[ORM\Column(name: 'min_select', type: 'smallint', options: ['default' => 0])]
    private int $minSelect = 0;

    /** `null` یعنی نامحدود. */
    #[ORM\Column(name: 'max_select', type: 'smallint', nullable: true)]
    private ?int $maxSelect = null;

    #[ORM\Column(name: 'sort_order', type: 'smallint', options: ['default' => 0])]
    private int $sortOrder = 0;

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

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

    /** @var Collection<int, ItemGroupMember> */
    #[ORM\OneToMany(targetEntity: ItemGroupMember::class, mappedBy: 'group', cascade: ['persist', 'remove'], orphanRemoval: true)]
    #[ORM\OrderBy(['sortOrder' => 'ASC'])]
    private Collection $members;

    public function __construct(ServiceItem $service, string $name)
    {
        $this->uuid      = Uuid::v4()->toRfc4122();
        $this->service   = $service;
        $this->name      = $name;
        $this->createdAt = time();
        $this->updatedAt = time();
        $this->members   = new ArrayCollection();

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

    public function getId(): ?int             { return $this->id; }
    public function getUuid(): string         { return $this->uuid; }
    public function getService(): ServiceItem { return $this->service; }
    public function getName(): string         { return $this->name; }
    public function getMinSelect(): int       { return $this->minSelect; }
    public function getMaxSelect(): ?int      { return $this->maxSelect; }
    public function getSortOrder(): int       { return $this->sortOrder; }

    /** @return Collection<int, ItemGroupMember> */
    public function getMembers(): Collection  { return $this->members; }

    public function setName(string $v): self   { $this->name = $v; $this->touch(); return $this; }
    public function setSortOrder(int $v): self { $this->sortOrder = $v; $this->touch(); return $this; }

    /** @throws \InvalidArgumentException روی بازهٔ ناممکن */
    public function setSelectRange(int $min, ?int $max): self
    {
        if ($min < 0) {
            throw new \InvalidArgumentException('min_select cannot be negative.');
        }

        if ($max !== null && $max < 1) {
            throw new \InvalidArgumentException('max_select must be at least 1 when set.');
        }

        if ($max !== null && $max < $min) {
            throw new \InvalidArgumentException('max_select cannot be smaller than min_select.');
        }

        $this->minSelect = $min;
        $this->maxSelect = $max;
        $this->touch();

        return $this;
    }

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

    public function toArray(): array
    {
        return [
            'uuid'         => $this->uuid,
            'service_uuid' => $this->service->getUuid(),
            'name'         => $this->name,
            'min_select'   => $this->minSelect,
            'max_select'   => $this->maxSelect,
            'sort_order'   => $this->sortOrder,
            'items'        => array_map(
                static fn (ItemGroupMember $m): array => $m->toArray(),
                $this->members->toArray(),
            ),
        ];
    }
}
