<?php

namespace App\ClinicService\Entity;

use App\ClinicService\Repository\ServiceItemRelationRepository;
use App\Shared\Tenant\TenantOwnedTrait;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Uid\Uuid;

/**
 * رابطهٔ دو آیتم: «بیکینی با فول‌بادی جمع نمی‌شود» یا «الف پیش‌نیاز ب است».
 *
 * بند ۵ مستند این‌ها را از موتور قوانین جدا می‌کند: هر دو دربارهٔ **انتخاب** آیتم‌اند،
 * نه دربارهٔ شرایط بیمار یا زمان، و باید پیش از هر محاسبه‌ای بررسی شوند.
 */
#[ORM\Entity(repositoryClass: ServiceItemRelationRepository::class)]
#[ORM\Table(name: 'service_item_relations')]
#[ORM\UniqueConstraint(name: 'uniq_relation_triple', columns: ['item_id', 'related_item_id', 'type'])]
#[ORM\Index(columns: ['entity_type', 'entity_id'], name: 'idx_relation_tenant')]
#[ORM\Index(columns: ['item_id', 'type'], name: 'idx_relation_item_type')]
class ServiceItemRelation
{
    use TenantOwnedTrait;

    public const TYPE_INCOMPATIBLE = 'incompatible_with';
    public const TYPE_REQUIRES     = 'requires';

    public const TYPES = [self::TYPE_INCOMPATIBLE, self::TYPE_REQUIRES];

    #[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: 'item_id', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')]
    private ServiceItem $item;

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

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

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

    public function __construct(ServiceItem $item, ServiceItem $relatedItem, string $type)
    {
        if (!in_array($type, self::TYPES, true)) {
            throw new \InvalidArgumentException(sprintf('Unknown relation type "%s".', $type));
        }

        if ($item === $relatedItem) {
            throw new \InvalidArgumentException('An item cannot relate to itself.');
        }

        $this->uuid        = Uuid::v4()->toRfc4122();
        $this->item        = $item;
        $this->relatedItem = $relatedItem;
        $this->type        = $type;
        $this->createdAt   = time();

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

    public function getId(): ?int                 { return $this->id; }
    public function getUuid(): string             { return $this->uuid; }
    public function getItem(): ServiceItem        { return $this->item; }
    public function getRelatedItem(): ServiceItem { return $this->relatedItem; }
    public function getType(): string             { return $this->type; }

    public function toArray(): array
    {
        return [
            'uuid'              => $this->uuid,
            'item_uuid'         => $this->item->getUuid(),
            'related_item_uuid' => $this->relatedItem->getUuid(),
            'related_item_name' => $this->relatedItem->getName(),
            'type'              => $this->type,
        ];
    }
}
