<?php


namespace Webapp\Core\Entity;

use ApiPlatform\Core\Annotation\ApiFilter;
use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\ExistsFilter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Core\Serializer\Filter\GroupFilter;
use DateTime;
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\CustomFilters\DeletedFilter;
use Shared\Authentication\Entity\IdentifiedEntity;
use Shared\Authentication\Entity\Site;
use Shared\Authentication\Entity\User;
use Shared\RightManagement\Annotation\AdvancedRight;
use Shared\RightManagement\Traits\HasOwnerEntity;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
use Webapp\Core\ApiOperation\RestoreObjectOperation;
use Webapp\Core\Dto\Protocol\ProtocolInputPostDto;
use Webapp\Core\Dto\Protocol\ProtocolInputPutDto;
use Webapp\Core\Entity\Attachment\ProtocolAttachment;

/**
 * Class Protocol
 * @package Webapp\Core\Entity
 *
 * @ApiResource(
 *     collectionOperations={
 *         "get"={"security"="is_granted('ROLE_PLATFORM_MANAGER')"},
 *         "post"={
 *              "security_post_denormalize"="is_granted('PROTOCOL_POST', object)",
 *              "input"=ProtocolInputPostDto::class,
 *          },
 *     },
 *     itemOperations={
 *          "get"={"security"="is_granted('ROLE_PLATFORM_MANAGER')"},
 *          "put"={
 *              "security"="object.getExperiment() === null && is_granted('ROLE_PLATFORM_MANAGER', object.getSite())",
 *              "input"=ProtocolInputPutDto::class,
 *          },
 *          "patch"={
 *              "security"="is_granted('ROLE_PLATFORM_MANAGER')",
 *              "denormalization_context"={"groups"={"edit"}}
 *          },
 *          "delete"={
 *              "security"="object.getExperiment() === null && is_granted('ROLE_PLATFORM_MANAGER', object.getSite())"
 *          },
 *          "restore"={
 *              "controller"=RestoreObjectOperation::class,
 *              "method"="PATCH",
 *              "path"="/protocols/{id}/restore",
 *              "security"="is_granted('ROLE_SITE_ADMIN')",
 *              "read"=false,
 *              "validate"=false,
 *              "openapi_context"={
 *                  "summary": "Restore deleted protocol",
 *                  "description": "Remove the deleted state"
 *              },
 *          }
 *     }
 * )
 * @ApiFilter(SearchFilter::class, properties={"experiment": "exact", "site": "exact", "name": "exact"})
 * @ApiFilter(ExistsFilter::class, properties={"deletedAt"})
 * @ApiFilter(GroupFilter::class, arguments={"whitelist"={"design_explorer_view", "protocol_full_view", "platform_full_view", "admin_explorer_view", "protocol_synthesis"}})
 * @ApiFilter(DeletedFilter::class)
 *
 * @AdvancedRight(classIdentifier="webapp_protocol", ownerField="owner", parentFields={"experiment"}, siteAttribute="site")
 *
 * @Gedmo\SoftDeleteable()
 *
 * @ORM\Entity()
 * @ORM\Table(name="protocol", schema="webapp")
 */
class Protocol extends IdentifiedEntity
{
    public const DEFAULT_PREFIX_FROM_CSV = "p-";
    public const DEFAULT_AIM_FROM_CSV = "";

    use HasOwnerEntity;

    use SoftDeleteableEntity;

    /**
     * @var string
     *
     * @ORM\Column(type="string")
     * @Groups({"design_explorer_view", "platform_full_view", "protocol_full_view", "admin_explorer_view", "admin_explorer_view", "edit", "platform_synthesis", "protocol_synthesis"})
     * @Assert\NotBlank
     */
    private string $name;

    /**
     * @var string
     *
     * @ORM\Column(type="text")
     * @Groups({"platform_full_view", "edit", "protocol_synthesis", "protocol_full_view"})
     */
    private string $aim;

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

    /**
     * @var DateTime $created
     *
     * @Gedmo\Timestampable(on="create")
     * @ORM\Column(type="datetime")
     * @Groups({"platform_full_view", "protocol_synthesis", "protocol_full_view"})
     */
    private DateTime $created;

    /**
     * @var Collection|array
     *
     * @ORM\OneToMany(targetEntity="Webapp\Core\Entity\Factor", mappedBy="protocol", cascade={"persist", "remove"}, orphanRemoval=true)
     * @Groups({"design_explorer_view", "platform_full_view", "protocol_synthesis", "protocol_full_view"})
     */
    private $factors;

    /**
     * @var Site|null
     *
     * @Groups({"protocol_synthesis"})
     * @ORM\ManyToOne(targetEntity="Shared\Authentication\Entity\Site", inversedBy="protocols")
     */
    private ?Site $site;

    /**
     * @var Collection|array
     *
     * @ORM\OneToMany(targetEntity="Webapp\Core\Entity\Treatment", mappedBy="protocol", cascade={"persist", "remove"}, orphanRemoval=true)
     * @Groups({"platform_full_view", "protocol_synthesis", "protocol_full_view"})
     */
    private $treatments;

    /**
     * @var ?User the owner of the entity
     *
     * @ORM\ManyToOne(targetEntity="Shared\Authentication\Entity\User")
     * @Groups({"design_explorer_view", "platform_full_view", "protocol_synthesis", "protocol_full_view"})
     */
    private ?User $owner;

    /**
     * @var ?Experiment
     *
     * @ORM\OneToOne(targetEntity="Webapp\Core\Entity\Experiment", inversedBy="protocol")
     */
    private ?Experiment $experiment;

    /**
     * @var Algorithm|null
     *
     * @ORM\ManyToOne(targetEntity="Webapp\Core\Entity\Algorithm", cascade={"persist"})
     * @Groups({"platform_full_view", "platform_synthesis", "protocol_synthesis", "protocol_full_view"})
     */
    private ?Algorithm $algorithm;

    /**
     * @var Collection<array-key, ProtocolAttachment>|array<ProtocolAttachment>
     * @ORM\OneToMany(targetEntity="Webapp\Core\Entity\Attachment\ProtocolAttachment", mappedBy="protocol", cascade={"persist", "remove"})
     */
    private $protocolAttachments;

    public function __construct()
    {
        $this->factors = new ArrayCollection();
        $this->treatments = new ArrayCollection();
        $this->comment = null;
        $this->site = null;
        $this->experiment = null;
        $this->owner = null;
        $this->algorithm = null;
        $this->protocolAttachments = new ArrayCollection();
    }

    /**
     * @Groups({"platform_full_view"})
     * @return mixed
     */
    public function getId()
    {
        return $this->id;
    }

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

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

    /**
     * @return string
     */
    public function getAim(): string
    {
        return $this->aim;
    }

    /**
     * @param string $aim
     * @return Protocol
     */
    public function setAim(string $aim): Protocol
    {
        $this->aim = $aim;
        return $this;
    }

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

    /**
     * @param string|null $comment
     * @return Protocol
     */
    public function setComment(?string $comment): Protocol
    {
        $this->comment = $comment;
        return $this;
    }

    /**
     * @return DateTime
     */
    public function getCreated(): DateTime
    {
        return $this->created;
    }

    /**
     * @param DateTime $created
     * @return Protocol
     */
    public function setCreated(DateTime $created): Protocol
    {
        $this->created = $created;
        return $this;
    }

    /**
     * @return array<Factor>|Collection<array-key,Factor>
     */
    public function getFactors()
    {
        return $this->factors;
    }

    /**
     * @param array<Factor>|Collection<array-key,Factor> $factors
     * @return Protocol
     */
    public function setFactors($factors)
    {
        $this->factors = $factors;
        foreach ($factors as $factor){
            $factor->setProtocol($this);
        }
        return $this;
    }

    /**
     * @param Factor $factor
     * @return Protocol
     */
    public function addFactors($factor)
    {
        if(!$this->factors->contains($factor)){
            $this->factors->add($factor);
            $factor->setProtocol($this);
        }
        return $this;
    }

    /**
     * @param Factor $factor
     * @return Protocol
     */
    public function removeFactors($factor)
    {
        if($this->factors->contains($factor)){
            $this->factors->removeElement($factor);
            $factor->setProtocol(null);
        }
        return $this;
    }

    /**
     * @return Site|null
     */
    public function getSite(): ?Site
    {
        return $this->site;
    }

    /**
     * @param Site|null $site
     * @return Protocol
     */
    public function setSite(?Site $site): Protocol
    {
        $this->site = $site;
        $this->experiment = null;
        return $this;
    }

    /**
     * @return array<Treatment>|Collection<array-key,Treatment>
     */
    public function getTreatments()
    {
        return $this->treatments;
    }

    /**
     * @param array<Treatment>|Collection $treatments
     * @return Protocol
     */
    public function setTreatments($treatments)
    {
        $this->treatments = $treatments;
        foreach ($treatments as $treatment) {
            $treatment->setProtocol($this);
        }
        return $this;
    }

    /**
     * @param Treatment $treatment
     * @return Protocol
     */
    public function addTreatments($treatment)
    {
        if(!$this->treatments->contains($treatment)){
            $this->treatments->add($treatment);
            $treatment->setProtocol($this);
        }
        return $this;
    }

    /**
     * @param Treatment $treatment
     * @return Protocol
     */
    public function removeTreatments($treatment)
    {
        if($this->treatments->contains($treatment)){
            $this->treatments->removeElement($treatment);
            $treatment->setProtocol(null);
        }
        return $this;
    }

    /**
     * @return Experiment|null
     */
    public function getExperiment(): ?Experiment
    {
        return $this->experiment;
    }

    /**
     * @param Experiment|null $experiment
     * @return Protocol
     */
    public function setExperiment(?Experiment $experiment): Protocol
    {
        $this->site = null;
        $this->experiment = $experiment;
        return $this;
    }

    /**
     * @return User|null
     */
    public function getOwner(): ?User
    {
        return $this->owner;
    }

    /**
     * @param User|null $owner
     * @return Protocol
     */
    public function setOwner(?User $owner): Protocol
    {
        $this->owner = $owner;
        return $this;
    }

    public function setDeletedAt(DateTime $deletedAt = null)
    {
        $this->deletedAt = $deletedAt;
        if($deletedAt === null){
            foreach ($this->getTreatments() as $child){
                $child->setDeletedAt($deletedAt);
            }
            foreach ($this->getFactors() as $child){
                $child->setDeletedAt($deletedAt);
            }
        }

        return $this;
    }

    /**
     * @return Algorithm|null
     */
    public function getAlgorithm(): ?Algorithm
    {
        return $this->algorithm;
    }

    /**
     * @param Algorithm|null $algorithm
     * @return Protocol
     */
    public function setAlgorithm(?Algorithm $algorithm): Protocol
    {
        $this->algorithm = $algorithm;
        return $this;
    }

    /**
     * @return Collection|ProtocolAttachment[]
     */
    public function getProtocolAttachments()
    {
        return $this->protocolAttachments;
    }

    /**
     * @param Collection|ProtocolAttachment[] $protocolAttachments
     * @return Protocol
     */
    public function setProtocolAttachments($protocolAttachments)
    {
        $this->protocolAttachments = $protocolAttachments;
        return $this;
    }

    /**
     * @param ProtocolAttachment $protocolAttachment
     * @return Protocol
     */
    public function addProtocolAttachment($protocolAttachment)
    {
        if (!$this->protocolAttachments->contains($protocolAttachment)) {
            $this->protocolAttachments->add($protocolAttachment);
            $protocolAttachment->setProtocol($this);
        }
        return $this;
    }

    /**
     * @param ProtocolAttachment $protocolAttachment
     * @return Protocol
     */
    public function removeProtocolAttachment($protocolAttachment)
    {
        if ($this->protocolAttachments->contains($protocolAttachment)) {
            $this->protocolAttachments->removeElement($protocolAttachment);
            $protocolAttachment->setProtocol(null);
        }
        return $this;
    }
}
