<?php

/*
 * @author TRYDEA - 2024
 */

namespace Webapp\Core\Entity;

use ApiPlatform\Core\Annotation\ApiFilter;
use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Annotation\ApiSubresource;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Core\Serializer\Filter\GroupFilter;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Gedmo\SoftDeleteable\Traits\SoftDeleteableEntity;
use Shared\Authentication\Entity\IdentifiedEntity;
use Shared\Utils\ArrayCollectionUtils;
use Symfony\Component\Serializer\Annotation\Groups;
use Webapp\Core\Annotation\GraphicallyDeletable;
use Webapp\Core\Traits\GraphicallyDeletableEntity;

/**
 * @ApiResource(
 *     collectionOperations={
 *          "get"={}
 *     },
 *     itemOperations={
 *          "get"={},
 *          "patch"={
 *              "security"="is_granted('ROLE_PLATFORM_MANAGER')",
 *              "denormalization_context"={"groups"={"edit"}}
 *          },
 *     }
 * )
 *
 * @ApiFilter(SearchFilter::class, properties={"block": "exact"})
 * @ApiFilter(GroupFilter::class, arguments={"whitelist"={"design_explorer_view", "platform_full_view", "parent_view"}})
 *
 * @Gedmo\SoftDeleteable()
 *
 * @GraphicallyDeletable()
 *
 * @ORM\Entity()
 *
 * @ORM\Table(name="sub_block", schema="webapp")
 */
class SubBlock extends IdentifiedEntity implements BusinessObject
{
    use GraphicallyDeletableEntity;

    use HasGeometryEntity;

    use OpenSilexEntity;

    use SoftDeleteableEntity;

    /**
     * @ORM\Column(type="string")
     *
     * @Groups({"design_explorer_view", "platform_full_view", "webapp_data_view", "change_report", "fusion_result"})
     */
    private string $number = '';

    /**
     * @var Collection<int, UnitPlot>
     *
     * @ORM\OneToMany(targetEntity="Webapp\Core\Entity\UnitPlot", cascade={"persist", "remove"}, mappedBy="subBlock")
     *
     * @Groups({"platform_full_view"})
     */
    private Collection $unitPlots;

    /**
     * @var Collection<int, SurfacicUnitPlot>
     *
     * @ORM\OneToMany(targetEntity="Webapp\Core\Entity\SurfacicUnitPlot", cascade={"persist", "remove"}, mappedBy="subBlock")
     *
     * @Groups({"platform_full_view"})
     */
    private Collection $surfacicUnitPlots;

    /**
     * @ORM\ManyToOne(targetEntity="Webapp\Core\Entity\Block", inversedBy="subBlocks")
     *
     * @Groups({"webapp_data_view", "parent_view", "change_report"})
     */
    private Block $block;

    /**
     * @var Collection<int, OutExperimentationZone>
     *
     * @ORM\OneToMany(targetEntity="Webapp\Core\Entity\OutExperimentationZone", mappedBy="subBlock", cascade={"persist", "remove"})
     *
     * @Groups({"platform_full_view"})
     */
    private Collection $outExperimentationZones;

    /**
     * @var Collection<int, Note>
     *
     * @ORM\OneToMany(targetEntity="Webapp\Core\Entity\Note", mappedBy="subBlockTarget", cascade={"persist", "remove"}, orphanRemoval=true)
     *
     * @Groups({"note_view"})
     *
     * @ApiSubresource()
     */
    private Collection $notes;

    /**
     * @ORM\Column(type="integer", nullable=true)
     *
     * @Groups({"platform_full_view"})
     */
    private ?int $color = null;

    /**
     * @ORM\Column(type="string", nullable=true)
     *
     * @Groups({"platform_full_view", "edit"})
     */
    private ?string $comment = null;

    public function __construct()
    {
        $this->unitPlots = new ArrayCollection();
        $this->surfacicUnitPlots = new ArrayCollection();
        $this->outExperimentationZones = new ArrayCollection();
        $this->notes = new ArrayCollection();
    }

    /**
     * @Groups({"platform_full_view"})
     *
     * @psalm-mutation-free
     */
    public function getId(): ?int
    {
        return parent::getId();
    }

    /**
     * @psalm-mutation-free
     */
    public function getNumber(): string
    {
        return $this->number;
    }

    /**
     * @return $this
     */
    public function setNumber(string $number): self
    {
        $this->number = $number;

        return $this;
    }

    /**
     * @return Collection<int,  UnitPlot>
     *
     * @psalm-mutation-free
     */
    public function getUnitPlots(): Collection
    {
        return $this->unitPlots;
    }

    /**
     * @param iterable<array-key,  UnitPlot> $unitPlots
     *
     * @return $this
     */
    public function setUnitPlots(iterable $unitPlots): self
    {
        ArrayCollectionUtils::update($this->unitPlots, $unitPlots, function (UnitPlot $unitPlot) {
            $unitPlot->setSubBlock($this);
        });

        return $this;
    }

    /**
     * @return $this
     */
    public function addUnitPlots(UnitPlot $unitPlot): self
    {
        if (!$this->unitPlots->contains($unitPlot)) {
            $this->unitPlots->add($unitPlot);
            $unitPlot->setSubBlock($this);
        }

        return $this;
    }

    /**
     * @return Collection<int,  SurfacicUnitPlot>
     *
     * @psalm-mutation-free
     */
    public function getSurfacicUnitPlots(): Collection
    {
        return $this->surfacicUnitPlots;
    }

    /**
     * @param iterable<int,  SurfacicUnitPlot> $surfacicUnitPlots
     *
     * @return $this
     */
    public function setSurfacicUnitPlots(iterable $surfacicUnitPlots): self
    {
        ArrayCollectionUtils::update($this->surfacicUnitPlots, $surfacicUnitPlots, function (SurfacicUnitPlot $surfacicUnitPlot) {
            $surfacicUnitPlot->setSubBlock($this);
        });

        return $this;
    }

    /**
     * @return $this
     */
    public function addSurfacicUnitPlots(SurfacicUnitPlot $unitPlot): self
    {
        if (!$this->surfacicUnitPlots->contains($unitPlot)) {
            $this->surfacicUnitPlots->add($unitPlot);
            $unitPlot->setSubBlock($this);
        }

        return $this;
    }

    /**
     * @psalm-mutation-free
     */
    public function getBlock(): Block
    {
        return $this->block;
    }

    /**
     * @return $this
     */
    public function setBlock(Block $block): self
    {
        $this->block = $block;

        return $this;
    }

    /**
     * @return Collection<int,  Note>
     *
     * @psalm-mutation-free
     */
    public function getNotes(): Collection
    {
        return $this->notes;
    }

    /**
     * @param iterable<int,  Note> $notes
     *
     * @return $this
     */
    public function setNotes(iterable $notes): self
    {
        ArrayCollectionUtils::update($this->notes, $notes, function (Note $note) {
            $note->setTarget($this);
        });

        return $this;
    }

    /**
     * @return $this
     */
    public function addNote(Note $note): self
    {
        if (!$this->notes->contains($note)) {
            $this->notes->add($note);
            $note->setTarget($this);
        }

        return $this;
    }

    /**
     * @return $this
     */
    public function removeNote(Note $note): self
    {
        if ($this->notes->contains($note)) {
            $this->notes->removeElement($note);
            $note->setTarget(null);
        }

        return $this;
    }

    /**
     * @return Collection<int,  OutExperimentationZone>
     *
     * @psalm-mutation-free
     */
    public function getOutExperimentationZones(): Collection
    {
        return $this->outExperimentationZones;
    }

    /**
     * @param iterable<int,  OutExperimentationZone> $outExperimentationZones
     *
     * @return $this
     */
    public function setOutExperimentationZones(iterable $outExperimentationZones): self
    {
        ArrayCollectionUtils::update($this->outExperimentationZones, $outExperimentationZones, function (OutExperimentationZone $outExperimentationZone) {
            $outExperimentationZone->setSubBlock($this);
        });

        return $this;
    }

    /**
     * @return $this
     */
    public function addOutExperimentationZone(OutExperimentationZone $outExperimentationZone): self
    {
        if (!$this->outExperimentationZones->contains($outExperimentationZone)) {
            $this->outExperimentationZones->add($outExperimentationZone);
            $outExperimentationZone->setSubBlock($this);
        }

        return $this;
    }

    /**
     * @return $this
     */
    public function removeOutExperimentationZone(OutExperimentationZone $outExperimentationZone): self
    {
        if ($this->outExperimentationZones->contains($outExperimentationZone)) {
            $this->outExperimentationZones->removeElement($outExperimentationZone);
            $outExperimentationZone->setSubBlock(null);
        }

        return $this;
    }

    /**
     * @psalm-mutation-free
     */
    public function getColor(): ?int
    {
        return $this->color;
    }

    /**
     * @return $this
     */
    public function setColor(?int $color): self
    {
        $this->color = $color;

        return $this;
    }

    /**
     * @psalm-mutation-free
     */
    public function getComment(): ?string
    {
        return $this->comment;
    }

    /**
     * @return $this
     */
    public function setComment(?string $comment): self
    {
        $this->comment = $comment;

        return $this;
    }

    /**
     * @return $this
     */
    public function setDeletedAt(?\DateTime $deletedAt = null): self
    {
        $this->deletedAt = $deletedAt;
        if (null === $deletedAt) {
            foreach ($this->children() as $child) {
                $child->setDeletedAt($deletedAt);
            }
            foreach ($this->getNotes() as $child) {
                $child->setDeletedAt($deletedAt);
            }
        }

        return $this;
    }

    /**
     * @psalm-mutation-free
     */
    public function parent(): Block
    {
        return $this->block;
    }

    /**
     * @return (UnitPlot|SurfacicUnitPlot|OutExperimentationZone)[]
     */
    public function children(): array
    {
        return [
            ...$this->unitPlots->getValues(),
            ...$this->surfacicUnitPlots->getValues(),
            ...$this->outExperimentationZones->getValues(),
        ];
    }
}
