<?php

namespace Webapp\Core\Entity;

use ApiPlatform\Core\Annotation\ApiFilter;
use ApiPlatform\Core\Annotation\ApiResource;
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 Shared\Authentication\Entity\IdentifiedEntity;
use Shared\Utils\ArrayCollectionUtils;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ApiResource(
 *     collectionOperations={
 *         "get"={},
 *         "post"={}
 *     },
 *     itemOperations={
 *         "get"={},
 *         "patch"={},
 *         "delete"={}
 *     }
 * )
 *
 * @ApiFilter(SearchFilter::class, properties={"project": "exact"})
 * @ApiFilter(GroupFilter::class, arguments={"whitelist"={"project_explorer_view"}})
 *
 * @ORM\Entity
 *
 * @ORM\Table(name="path_base", schema="webapp")
 */
class PathBase extends IdentifiedEntity
{
    /**
     * @ORM\Column(type="string", nullable=false)
     *
     * @Assert\NotBlank
     *
     * @Groups({"project_explorer_view"})
     */
    private string $name = '';

    /**
     * @ORM\OneToOne(targetEntity="Webapp\Core\Entity\Project", inversedBy="pathBase")
     *
     * @Assert\NotBlank
     */
    private Project $project;

    /**
     * @var Collection<int, PathFilterNode>
     *
     * @ORM\OneToMany(targetEntity="Webapp\Core\Entity\PathFilterNode", mappedBy="pathBase", cascade={"remove", "persist"}, orphanRemoval=true)
     */
    private Collection $pathFilterNodes;

    /**
     * @ORM\Column(type="boolean")
     */
    private bool $askWhenEntering = false;

    /**
     * @var Collection<int, PathLevelAlgorithm>
     *
     * @Groups({"variable_synthesis"})
     *
     * @ORM\OneToMany(targetEntity="Webapp\Core\Entity\PathLevelAlgorithm", mappedBy="pathBase", cascade={"remove", "persist"}, orphanRemoval=true)
     */
    private Collection $pathLevelAlgorithms;

    /**
     * @var string[]
     *
     * @ORM\Column(type="simple_array", nullable=true)
     */
    private array $selectedIris = [];

    /**
     * @var string[]
     *
     * @ORM\Column(type="simple_array", nullable=true)
     */
    private array $orderedIris = [];

    /**
     * @var Collection<int, PathUserWorkflow>
     *
     * @Groups({"project_explorer_view", "project_synthesis", "variable_synthesis"})
     *
     * @ORM\OneToMany(targetEntity="Webapp\Core\Entity\PathUserWorkflow", mappedBy="pathBase", cascade={"remove", "persist"})
     */
    private Collection $userPaths;

    public function __construct()
    {
        $this->pathFilterNodes = new ArrayCollection();
        $this->pathLevelAlgorithms = new ArrayCollection();
        $this->userPaths = new ArrayCollection();
    }

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

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

        return $this;
    }

    /**
     * @psalm-mutation-free
     */
    public function getProject(): Project
    {
        return $this->project;
    }

    /**
     * @return $this
     */
    public function setProject(Project $project): self
    {
        $this->project = $project;

        return $this;
    }

    /**
     * @return Collection<int,  PathFilterNode>
     *
     * @psalm-mutation-free
     */
    public function getPathFilterNodes(): Collection
    {
        return $this->pathFilterNodes;
    }

    /**
     * @param iterable<int,  PathFilterNode> $pathFilterNodes
     *
     * @return $this
     */
    public function setPathFilterNodes(iterable $pathFilterNodes): self
    {
        ArrayCollectionUtils::update($this->pathFilterNodes, $pathFilterNodes, function (PathFilterNode $pathFilterNode) {
            $pathFilterNode->setPathBase($this);
        });

        return $this;
    }

    /**
     * @return $this
     */
    public function addPathFilterNode(PathFilterNode $pathFilterNode): self
    {
        if (!$this->pathFilterNodes->contains($pathFilterNode)) {
            $this->pathFilterNodes->add($pathFilterNode);
            $pathFilterNode->setPathBase($this);
        }

        return $this;
    }

    /**
     * @return $this
     */
    public function removePathFilterNode(PathFilterNode $pathFilterNode): self
    {
        if ($this->pathFilterNodes->contains($pathFilterNode)) {
            $this->pathFilterNodes->removeElement($pathFilterNode);
            $pathFilterNode->setPathBase(null);
        }

        return $this;
    }

    /**
     * @psalm-mutation-free
     */
    public function isAskWhenEntering(): bool
    {
        return $this->askWhenEntering;
    }

    /**
     * @return $this
     */
    public function setAskWhenEntering(bool $askWhenEntering): self
    {
        $this->askWhenEntering = $askWhenEntering;

        return $this;
    }

    /**
     * @return Collection<int,  PathLevelAlgorithm>
     *
     * @psalm-mutation-free
     */
    public function getPathLevelAlgorithms(): Collection
    {
        return $this->pathLevelAlgorithms;
    }

    /**
     * @param iterable<int,  PathLevelAlgorithm> $pathLevelAlgorithms
     *
     * @return $this
     */
    public function setPathLevelAlgorithms(iterable $pathLevelAlgorithms): self
    {
        ArrayCollectionUtils::update($this->pathLevelAlgorithms, $pathLevelAlgorithms, function (PathLevelAlgorithm $pathLevelAlgorithm): void {
            $pathLevelAlgorithm->setPathBase($this);
        });

        return $this;
    }

    /**
     * @return $this
     */
    public function addPathLevelAlgorithm(PathLevelAlgorithm $pathLevelAlgorithm): self
    {
        if (!$this->pathLevelAlgorithms->contains($pathLevelAlgorithm)) {
            $this->pathLevelAlgorithms->add($pathLevelAlgorithm);
            $pathLevelAlgorithm->setPathBase($this);
        }

        return $this;
    }

    /**
     * @return $this
     */
    public function removePathLevelAlgorithm(PathLevelAlgorithm $pathLevelAlgorithm): self
    {
        if ($this->pathLevelAlgorithms->contains($pathLevelAlgorithm)) {
            $this->pathLevelAlgorithms->removeElement($pathLevelAlgorithm);
            $pathLevelAlgorithm->setPathBase(null);
        }

        return $this;
    }

    /**
     * @return string[]
     *
     * @psalm-mutation-free
     */
    public function getSelectedIris(): array
    {
        return $this->selectedIris;
    }

    /**
     * @param string[] $selectedIris
     *
     * @return $this
     */
    public function setSelectedIris(array $selectedIris): self
    {
        $this->selectedIris = $selectedIris;

        return $this;
    }

    /**
     * @return string[]
     *
     * @psalm-mutation-free
     */
    public function getOrderedIris(): array
    {
        return $this->orderedIris;
    }

    /**
     * @param string[] $orderedIris
     *
     * @return $this
     */
    public function setOrderedIris(array $orderedIris): self
    {
        $this->orderedIris = $orderedIris;

        return $this;
    }

    /**
     * @return Collection<int,  PathUserWorkflow>
     *
     * @psalm-mutation-free
     */
    public function getUserPaths(): Collection
    {
        return $this->userPaths;
    }

    /**
     * @param iterable<int,  PathUserWorkflow> $userPaths
     *
     * @return $this
     */
    public function setUserPaths(iterable $userPaths): self
    {
        ArrayCollectionUtils::update($this->userPaths, $userPaths, function (PathUserWorkflow $pathUserWorkflow) {
            $pathUserWorkflow->setPathBase($this);
        });

        return $this;
    }

    /**
     * @return $this
     */
    public function addPathUserWorkflow(PathUserWorkflow $pathUserWorkflow): self
    {
        if (!$this->userPaths->contains($pathUserWorkflow)) {
            $this->userPaths->add($pathUserWorkflow);
            $pathUserWorkflow->setPathBase($this);
        }

        return $this;
    }

    /**
     * @return $this
     */
    public function removePathUserWorkflow(PathUserWorkflow $pathUserWorkflow): self
    {
        if ($this->userPaths->contains($pathUserWorkflow)) {
            $this->userPaths->removeElement($pathUserWorkflow);
        }

        return $this;
    }
}
