<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\TicketBundle\Entity;

use Chill\DocStoreBundle\Entity\StoredObject;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ReadableCollection;
use Doctrine\Common\Collections\Selectable;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation as Serializer;

#[ORM\Entity()]
#[ORM\Table(name: 'motive', schema: 'chill_ticket')]
#[Serializer\DiscriminatorMap(typeProperty: 'type', mapping: ['ticket_motive' => Motive::class])]
class Motive
{
    #[ORM\Id]
    #[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, nullable: false)]
    #[ORM\GeneratedValue(strategy: 'AUTO')]
    private ?int $id = null;

    #[ORM\Column(type: \Doctrine\DBAL\Types\Types::JSON, nullable: false, options: ['default' => '[]'])]
    private array $label = [];

    #[ORM\Column(type: \Doctrine\DBAL\Types\Types::BOOLEAN, nullable: false, options: ['default' => true])]
    private bool $active = true;

    #[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, nullable: true, enumType: EmergencyStatusEnum::class)]
    private ?EmergencyStatusEnum $makeTicketEmergency = null;

    /**
     * @var Collection<int, StoredObject>
     */
    #[ORM\ManyToMany(targetEntity: StoredObject::class, fetch: 'EAGER')]
    #[ORM\JoinTable(name: 'motive_stored_objects', schema: 'chill_ticket')]
    private Collection $storedObjects;

    #[ORM\Column(type: \Doctrine\DBAL\Types\Types::JSON, nullable: false, options: ['jsonb' => true, 'default' => '[]'])]
    private array $supplementaryComments = [];

    #[ORM\ManyToOne(targetEntity: Motive::class, inversedBy: 'children')]
    private ?Motive $parent = null;

    /**
     * @var Collection<int, Motive>&Selectable<int, Motive>
     */
    #[ORM\OneToMany(mappedBy: 'parent', targetEntity: Motive::class)]
    private Collection&Selectable $children;

    #[ORM\Column(name: 'ordering', type: \Doctrine\DBAL\Types\Types::FLOAT, nullable: true, options: ['default' => '0.0'])]
    private float $ordering = 0;

    public function __construct()
    {
        $this->storedObjects = new ArrayCollection();
        $this->children = new ArrayCollection();
    }

    public function addStoredObject(StoredObject $storedObject): void
    {
        if (!$this->storedObjects->contains($storedObject)) {
            $this->storedObjects[] = $storedObject;
        }
    }

    public function removeStoredObject(StoredObject $storedObject): void
    {
        $this->storedObjects->removeElement($storedObject);
    }

    public function getStoredObjects(): ReadableCollection
    {
        return $this->storedObjects;
    }

    public function isActive(): bool
    {
        return $this->active;
    }

    public function setActive(bool $active): void
    {
        $this->active = $active;
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getLabel(): array
    {
        return $this->label;
    }

    public function setLabel(array $label): void
    {
        $this->label = $label;
    }

    public function getMakeTicketEmergency(): ?EmergencyStatusEnum
    {
        return $this->makeTicketEmergency;
    }

    public function setMakeTicketEmergency(?EmergencyStatusEnum $makeTicketEmergency): void
    {
        $this->makeTicketEmergency = $makeTicketEmergency;
    }

    public function isMakeTicketEmergency(): bool
    {
        return null !== $this->makeTicketEmergency;
    }

    /**
     * Get the supplementary comments.
     *
     * @return array<array{label: string}>
     */
    public function getSupplementaryComments(): array
    {
        return $this->supplementaryComments;
    }

    /**
     * @param array{label: string} $supplementaryComments
     */
    public function addSupplementaryComment(array $supplementaryComments): void
    {
        $this->supplementaryComments[] = $supplementaryComments;
    }

    public function setSupplementaryComment(array $supplementaryComments): void
    {
        foreach ($this->supplementaryComments as $key => $supplementaryComment) {
            unset($this->supplementaryComments[$key]);
        }
        foreach (array_values($supplementaryComments) as $key => $supplementaryComment) {
            $this->supplementaryComments[$key] = $supplementaryComment;
        }
    }

    public function isParent(): bool
    {
        return $this->children->count() > 0;
    }

    public function isChild(): bool
    {
        return null !== $this->parent;
    }

    public function setParent(?Motive $parent): void
    {
        if (null !== $parent) {
            $parent->addChild($this);
        } else {
            $this->parent->removeChild($this);
        }

        $this->parent = $parent;
    }

    /**
     * @internal use @see{setParent} instead
     */
    public function addChild(Motive $child): void
    {
        if (!$this->children->contains($child)) {
            $this->children->add($child);
        }
    }

    /**
     * @internal use @see{setParent} with null as argument instead
     */
    public function removeChild(Motive $child): void
    {
        $this->children->removeElement($child);
    }

    public function getChildren(): ReadableCollection&Selectable
    {
        return $this->children;
    }

    public function getParent(): ?Motive
    {
        return $this->parent;
    }

    /**
     * Get the descendants of the current entity.
     *
     * This method collects all descendant entities recursively, starting from the current entity
     * and including all of its children and their descendants.
     *
     * @return ReadableCollection&Selectable A collection containing the current entity and all its descendants
     */
    public function getDescendants(): ReadableCollection&Selectable
    {
        $collection = new ArrayCollection([$this]);

        foreach ($this->getChildren() as $child) {
            foreach ($child->getDescendants() as $descendant) {
                $collection->add($descendant);
            }
        }

        return $collection;
    }

    public function getOrdering(): float
    {
        return $this->ordering;
    }

    public function setOrdering(float $ordering): void
    {
        $this->ordering = $ordering;
    }
}
