<?php

declare(strict_types=1);

namespace App\Entity;

use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Put;
use App\Entity\Trait\CuidEntityTrait;
use App\Filter\ConstructeurSearchFilter;
use App\Repository\ConstructeurRepository;
use DateTimeImmutable;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Attribute\Groups;
use Symfony\Component\Validator\Constraints as Assert;

#[UniqueEntity(fields: ['name'], message: 'Un fournisseur avec ce nom existe déjà.')]
#[ORM\Entity(repositoryClass: ConstructeurRepository::class)]
#[ORM\Table(name: 'constructeurs')]
#[ORM\HasLifecycleCallbacks]
#[ApiFilter(ConstructeurSearchFilter::class)]
#[ApiFilter(SearchFilter::class, properties: ['categories.id' => 'exact'])]
#[ApiFilter(OrderFilter::class, properties: ['name', 'email', 'createdAt'])]
#[ApiResource(
    description: 'Fournisseurs et constructeurs. Référentiel partagé entre les machines, pièces, composants et produits pour identifier les fabricants et distributeurs.',
    operations: [
        new Get(security: "is_granted('ROLE_VIEWER')"),
        new GetCollection(security: "is_granted('ROLE_VIEWER')"),
        new Post(security: "is_granted('ROLE_GESTIONNAIRE')"),
        new Put(security: "is_granted('ROLE_GESTIONNAIRE')"),
        new Patch(security: "is_granted('ROLE_GESTIONNAIRE')"),
        new Delete(security: "is_granted('ROLE_GESTIONNAIRE')"),
    ],
    paginationClientItemsPerPage: true,
    paginationMaximumItemsPerPage: 2000,
    normalizationContext: ['groups' => ['constructeur:read']],
    denormalizationContext: ['groups' => ['constructeur:write']]
)]
class Constructeur
{
    use CuidEntityTrait;

    #[ORM\Id]
    #[ORM\Column(type: Types::STRING, length: 36)]
    #[Groups(['constructeur:read'])]
    private ?string $id = null;

    #[ORM\Column(type: Types::STRING, length: 255, unique: true)]
    #[Assert\NotBlank(message: 'Le nom est obligatoire.')]
    #[Groups(['constructeur:read', 'constructeur:write'])]
    private ?string $name = null;

    #[ORM\Column(type: Types::STRING, length: 255, nullable: true)]
    #[Groups(['constructeur:read', 'constructeur:write'])]
    private ?string $email = null;

    #[ORM\Column(type: Types::DATETIME_IMMUTABLE, name: 'createdAt')]
    #[Groups(['constructeur:read'])]
    private DateTimeImmutable $createdAt;

    #[ORM\Column(type: Types::DATETIME_IMMUTABLE, name: 'updatedAt')]
    #[Groups(['constructeur:read'])]
    private DateTimeImmutable $updatedAt;

    /**
     * @var Collection<int, ConstructeurTelephone>
     */
    #[ORM\OneToMany(mappedBy: 'constructeur', targetEntity: ConstructeurTelephone::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
    #[Groups(['constructeur:read', 'constructeur:write'])]
    private Collection $telephones;

    /**
     * @var Collection<int, ConstructeurCategorie>
     */
    #[ORM\ManyToMany(targetEntity: ConstructeurCategorie::class, inversedBy: 'constructeurs')]
    #[ORM\JoinTable(name: 'constructeur_categories')]
    #[ORM\JoinColumn(name: 'constructeur_id', referencedColumnName: 'id', onDelete: 'CASCADE')]
    #[ORM\InverseJoinColumn(name: 'categorie_id', referencedColumnName: 'id', onDelete: 'CASCADE')]
    #[Groups(['constructeur:read', 'constructeur:write'])]
    private Collection $categories;

    /**
     * @var Collection<int, MachineConstructeurLink>
     */
    #[ORM\OneToMany(mappedBy: 'constructeur', targetEntity: MachineConstructeurLink::class, cascade: ['remove'])]
    private Collection $machineLinks;

    /**
     * @var Collection<int, ComposantConstructeurLink>
     */
    #[ORM\OneToMany(mappedBy: 'constructeur', targetEntity: ComposantConstructeurLink::class, cascade: ['remove'])]
    private Collection $composantLinks;

    /**
     * @var Collection<int, PieceConstructeurLink>
     */
    #[ORM\OneToMany(mappedBy: 'constructeur', targetEntity: PieceConstructeurLink::class, cascade: ['remove'])]
    private Collection $pieceLinks;

    /**
     * @var Collection<int, ProductConstructeurLink>
     */
    #[ORM\OneToMany(mappedBy: 'constructeur', targetEntity: ProductConstructeurLink::class, cascade: ['remove'])]
    private Collection $productLinks;

    public function __construct()
    {
        $this->createdAt      = new DateTimeImmutable();
        $this->updatedAt      = new DateTimeImmutable();
        $this->machineLinks   = new ArrayCollection();
        $this->composantLinks = new ArrayCollection();
        $this->pieceLinks     = new ArrayCollection();
        $this->productLinks   = new ArrayCollection();
        $this->telephones     = new ArrayCollection();
        $this->categories     = new ArrayCollection();
    }

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

    public function setName(string $name): static
    {
        $this->name = $name;

        return $this;
    }

    public function getEmail(): ?string
    {
        return $this->email;
    }

    public function setEmail(?string $email): static
    {
        $this->email = $email;

        return $this;
    }

    /**
     * @return Collection<int, ConstructeurTelephone>
     */
    public function getTelephones(): Collection
    {
        return $this->telephones;
    }

    public function addTelephone(ConstructeurTelephone $telephone): static
    {
        if (!$this->telephones->contains($telephone)) {
            $this->telephones->add($telephone);
            $telephone->setConstructeur($this);
        }

        return $this;
    }

    public function removeTelephone(ConstructeurTelephone $telephone): static
    {
        if ($this->telephones->removeElement($telephone)) {
            if ($telephone->getConstructeur() === $this) {
                $telephone->setConstructeur(null);
            }
        }

        return $this;
    }

    /**
     * @return Collection<int, ConstructeurCategorie>
     */
    public function getCategories(): Collection
    {
        return $this->categories;
    }

    public function addCategory(ConstructeurCategorie $category): static
    {
        if (!$this->categories->contains($category)) {
            $this->categories->add($category);
        }

        return $this;
    }

    public function removeCategory(ConstructeurCategorie $category): static
    {
        $this->categories->removeElement($category);

        return $this;
    }
}
