<?php

namespace App\Appointment\Plan\Entity;

use App\Appointment\Plan\Repository\SegmentRequirementRepository;
use App\Resource\Entity\ResourceType;
use App\Resource\Entity\Skill;
use Doctrine\ORM\Mapping as ORM;

/**
 * «این بخش چه منبعی می‌خواهد» — نقش، تعداد، مهارت لازم، قید، و نوع اشغال.
 *
 * فرزند aggregate با ریشهٔ {@see SegmentTemplate} که خودش جفت محیط دارد؛ uuid از
 * request نمی‌گیرد و فقط از `PUT /segment-template/{uuid}/requirements` نوشته می‌شود.
 */
#[ORM\Entity(repositoryClass: SegmentRequirementRepository::class)]
#[ORM\Table(name: 'segment_requirements')]
#[ORM\Index(columns: ['segment_id'], name: 'idx_requirement_segment')]
class SegmentRequirement
{
    /** منبع در تمام بخش قفل است. */
    public const OCCUPANCY_EXCLUSIVE = 'exclusive';

    /** منبع لازم است ولی می‌تواند هم‌زمان جای دیگری هم باشد (ظرفیتش می‌شمارد). */
    public const OCCUPANCY_SHARED = 'shared';

    public const OCCUPANCIES = [self::OCCUPANCY_EXCLUSIVE, self::OCCUPANCY_SHARED];

    /** جنسیت منبع باید با بیمار یکی باشد — بند ۱۰ مستند. */
    public const CONSTRAINT_SAME_GENDER = 'same_gender_as_patient';

    public const CONSTRAINTS = [self::CONSTRAINT_SAME_GENDER];

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

    #[ORM\ManyToOne(targetEntity: SegmentTemplate::class, inversedBy: 'requirements')]
    #[ORM\JoinColumn(name: 'segment_id', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')]
    private SegmentTemplate $segment;

    #[ORM\ManyToOne(targetEntity: ResourceType::class)]
    #[ORM\JoinColumn(name: 'resource_type_id', referencedColumnName: 'id', nullable: false, onDelete: 'RESTRICT')]
    private ResourceType $resourceType;

    #[ORM\ManyToOne(targetEntity: Skill::class)]
    #[ORM\JoinColumn(name: 'skill_id', referencedColumnName: 'id', nullable: true, onDelete: 'RESTRICT')]
    private ?Skill $skill = null;

    #[ORM\Column(type: 'smallint', options: ['default' => 1])]
    private int $count = 1;

    #[ORM\Column(type: 'string', length: 12, options: ['default' => self::OCCUPANCY_EXCLUSIVE])]
    private string $occupancy = self::OCCUPANCY_EXCLUSIVE;

    /** @var string[] */
    #[ORM\Column(type: 'json', nullable: true)]
    private ?array $constraints = null;

    public function __construct(SegmentTemplate $segment, ResourceType $resourceType, int $count = 1)
    {
        if ($count < 1) {
            throw new \InvalidArgumentException('A requirement needs at least one resource.');
        }

        $this->segment      = $segment;
        $this->resourceType = $resourceType;
        $this->count        = $count;
    }

    public function getId(): ?int                   { return $this->id; }
    public function getSegment(): SegmentTemplate   { return $this->segment; }
    public function getResourceType(): ResourceType { return $this->resourceType; }
    public function getSkill(): ?Skill              { return $this->skill; }
    public function getCount(): int                 { return $this->count; }
    public function getOccupancy(): string          { return $this->occupancy; }

    /** @return string[] */
    public function getConstraints(): array { return $this->constraints ?? []; }

    public function setSkill(?Skill $v): self { $this->skill = $v; return $this; }

    public function setOccupancy(string $v): self
    {
        if (!in_array($v, self::OCCUPANCIES, true)) {
            throw new \InvalidArgumentException(sprintf('Unknown occupancy "%s".', $v));
        }

        $this->occupancy = $v;

        return $this;
    }

    /** @param string[] $constraints */
    public function setConstraints(array $constraints): self
    {
        foreach ($constraints as $constraint) {
            if (!in_array($constraint, self::CONSTRAINTS, true)) {
                throw new \InvalidArgumentException(sprintf('Unknown constraint "%s".', $constraint));
            }
        }

        $this->constraints = $constraints === [] ? null : array_values(array_unique($constraints));

        return $this;
    }

    public function requiresSameGender(): bool
    {
        return in_array(self::CONSTRAINT_SAME_GENDER, $this->getConstraints(), true);
    }

    public function toArray(): array
    {
        return [
            'role'        => $this->resourceType->getCode(),
            'role_name'   => $this->resourceType->getName(),
            'type_uuid'   => $this->resourceType->getUuid(),
            'skill_uuid'  => $this->skill?->getUuid(),
            'skill_name'  => $this->skill?->getName(),
            'count'       => $this->count,
            'occupancy'   => $this->occupancy,
            'constraints' => $this->getConstraints(),
        ];
    }
}
