<?php

declare(strict_types=1);

/*
 * This file is part of the VITICORE2 package.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace CpCreation\VitiCore\Product\Model;

use CpCreation\VitiCore\Award\Model\Award;
use CpCreation\VitiCore\Behavior\HasDisable;
use CpCreation\VitiCore\Behavior\HasFile;
use CpCreation\VitiCore\Behavior\HasImage;
use CpCreation\VitiCore\Behavior\HasTimestamp;
use CpCreation\VitiCore\Behavior\HasTranslatable;
use CpCreation\VitiCore\Behavior\Impl\Disable;
use CpCreation\VitiCore\Behavior\Impl\File;
use CpCreation\VitiCore\Behavior\Impl\Image;
use CpCreation\VitiCore\Behavior\Impl\SoftDeletable;
use CpCreation\VitiCore\Behavior\Impl\Timestamp;
use CpCreation\VitiCore\Behavior\Impl\Translatable;
use CpCreation\VitiCore\Behavior\IsSoftDeletable;
use CpCreation\VitiCore\Media\Model\Image as ModelImage;
use CpCreation\VitiCore\Product\Repository\ProductRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Gedmo\Sortable\Sortable;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Webmozart\Assert\Assert as AssertDomain;

#[ORM\Entity(repositoryClass: ProductRepository::class)]
#[ORM\Table(name: 'product_product')]
#[ORM\HasLifecycleCallbacks]
class Product implements HasTimestamp, HasImage, Sortable, HasDisable, HasFile, HasTranslatable, IsSoftDeletable
{
    use Disable;
    use File;
    use Image;
    use SoftDeletable;
    use Timestamp;
    use Translatable;

    #[ORM\Id]
    #[ORM\Column(type: 'uuid')]
    #[ORM\GeneratedValue(strategy: 'NONE')]
    private UuidInterface $id;

    #[ORM\Column(type: 'boolean')]
    private bool $cuvee;

    #[ORM\Column(type: 'boolean')]
    private bool $shop;

    #[ORM\Column(type: 'boolean')]
    private bool $favorite;

    #[ORM\Column(type: 'integer')]
    #[Gedmo\SortablePosition]
    private ?int $position = null;

    #[ORM\Column(type: 'integer', nullable: true)]
    private ?int $positionShop = null;

    #[ORM\Column(type: 'boolean')]
    private bool $new;

    #[ORM\Column(type: 'boolean')]
    private bool $soldOut;

    #[ORM\Column(type: 'text', nullable: true, length: 8)]
    private ?string $ribbonCustomColor = null;

    #[ORM\Column(type: 'boolean')]
    private bool $enableColor;

    #[ORM\Column(type: 'boolean')]
    private bool $enableColorQuality;

    #[ORM\Column(type: 'text', nullable: true, length: 8)]
    private ?string $color = null;

    #[ORM\Column(type: 'text', nullable: true, length: 8)]
    private ?string $colorGradient1 = null;

    #[ORM\Column(type: 'text', nullable: true, length: 8)]
    private ?string $colorGradient2 = null;

    #[ORM\Column(type: 'integer', nullable: true, length: 8)]
    private ?int $orderInFavorites = null;

    #[ORM\ManyToOne(targetEntity: ProductCategory::class, inversedBy: 'products')]
    #[ORM\JoinColumn(name: 'product_category_id', referencedColumnName: 'id', nullable: true)]
    #[Assert\NotBlank]
    private ?ProductCategory $category = null;

    /**
     * @var Collection<int, ProductVariant>
     */
    #[ORM\OneToMany(targetEntity: ProductVariant::class, mappedBy: 'product', cascade: ['persist', 'remove'], orphanRemoval: true)]
    #[ORM\OrderBy(['position' => 'ASC'])]
    #[Assert\Count(min: 1, minMessage: 'Vous devez ajouter au moins un variant de produit')]
    #[Assert\Valid]
    private Collection $variants;

    /**
     * @var Collection<int, Award>
     */
    #[ORM\ManyToMany(targetEntity: Award::class)]
    #[Assert\Valid]
    private Collection $awards;

    #[ORM\OneToOne(targetEntity: ModelImage::class, cascade: ['all'])]
    #[ORM\JoinColumn(name: 'second_image_id', nullable: true, referencedColumnName: 'id', onDelete: 'SET NULL')]
    private ?ModelImage $secondImage = null;

    #[ORM\OneToOne(targetEntity: ModelImage::class, cascade: ['all'])]
    #[ORM\JoinColumn(name: 'background_image_id', nullable: true, referencedColumnName: 'id', onDelete: 'SET NULL')]
    private ?ModelImage $backgroundImage = null;

    /**
     * @var Collection<int, ProductImage>
     */
    #[ORM\OneToMany(targetEntity: ProductImage::class, cascade: ['persist', 'remove'], mappedBy: 'product', orphanRemoval: true)]
    #[ORM\OrderBy(['position' => 'ASC'])]
    #[Assert\Valid]
    private Collection $images;

    /**
     * @var Collection<int, ProductFile>
     */
    #[ORM\OneToMany(targetEntity: ProductFile::class, cascade: ['persist', 'remove'], mappedBy: 'product', orphanRemoval: true)]
    #[ORM\OrderBy(['locale' => 'ASC'])]
    #[Assert\Valid]
    private Collection $files;

    /**
     * @var Collection<int, ProductPriceGroup>
     */
    #[ORM\ManyToMany(targetEntity: ProductPriceGroup::class)]
    private Collection $groups;

    private array $degressivities = [];

    #[ORM\Column(type: 'text', nullable: true, length: 8)]
    private ?string $referenceToken = null;

    public function __construct()
    {
        $this->id = Uuid::uuid4();
        $this->variants = new ArrayCollection();
        $this->awards = new ArrayCollection();
        $this->groups = new ArrayCollection();
        $this->images = new ArrayCollection();
        $this->files = new ArrayCollection();
        $this->disabled = false;
        $this->cuvee = true;
        $this->shop = true;
        $this->favorite = false;
        $this->new = false;
        $this->soldOut = false;
        $this->enableColor = false;
        $this->enableColorQuality = false;
    }

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

    public function setId(Uuid $id): void
    {
        $this->id = $id;
    }

    public function isCuvee(): bool
    {
        return $this->cuvee;
    }

    public function setCuvee(bool $cuvee = true): void
    {
        $this->cuvee = $cuvee;
    }

    public function isShop(): bool
    {
        return $this->shop;
    }

    public function setShop(bool $shop): void
    {
        $this->shop = $shop;
    }

    public function isFavorite(): bool
    {
        return $this->favorite;
    }

    public function setFavorite(bool $favorite): void
    {
        $this->favorite = $favorite;
    }

    public function getPosition(): ?int
    {
        return $this->position;
    }

    public function getPositionShop(): ?int
    {
        return $this->positionShop;
    }

    public function isNew(): bool
    {
        return $this->new;
    }

    public function setNew(bool $new): void
    {
        $this->new = $new;
    }

    public function isSoldOut(): bool
    {
        return $this->soldOut;
    }

    public function setSoldOut(bool $soldOut): void
    {
        $this->soldOut = $soldOut;
    }

    public function getRibbonCustomColor(): ?string
    {
        return $this->ribbonCustomColor;
    }

    public function setRibbonCustomColor(?string $ribbonCustomColor): void
    {
        $this->ribbonCustomColor = $ribbonCustomColor;
    }

    public function isEnableColor(): bool
    {
        return $this->enableColor;
    }

    public function setEnableColor(bool $enableColor): void
    {
        $this->enableColor = $enableColor;
    }

    public function isEnableColorQuality(): bool
    {
        return $this->enableColorQuality;
    }

    public function setEnableColorQuality(bool $enableColorQuality): void
    {
        $this->enableColorQuality = $enableColorQuality;
    }

    public function getColor(): ?string
    {
        return $this->color;
    }

    public function setColor(?string $color): void
    {
        $this->color = $color;
    }

    public function setPosition(?int $position): void
    {
        $this->position = $position;
    }

    public function setPositionShop(?int $positionShop): void
    {
        $this->positionShop = $positionShop;
    }

    public function getOrderInFavorites(): ?int
    {
        return $this->orderInFavorites;
    }

    public function setOrderInFavorites(?int $orderInFavorites): void
    {
        $this->orderInFavorites = $orderInFavorites;
    }

    public function getCategory(): ?ProductCategory
    {
        return $this->category;
    }

    public function setCategory(?ProductCategory $category): void
    {
        $this->category = $category;
    }

    public function getBackgroundImage(): ?ModelImage
    {
        return $this->backgroundImage;
    }

    public function setBackgroundImage(?ModelImage $backgroundImage): void
    {
        $this->backgroundImage = $backgroundImage;
    }

    public function addVariant(ProductVariant $variant): self
    {
        $variant->setProduct($this);
        $this->variants[] = $variant;

        return $this;
    }

    public function removeVariant(ProductVariant $variant): self
    {
        $variant->markDeleted();
        $this->variants[] = $variant;

        return $this;
    }

    /**
     * @return Collection<int, ProductVariant>
     */
    public function getVariants(): Collection
    {
        return $this->variants;
    }

    public function addAward(Award $award): void
    {
        $this->awards[] = $award;
    }

    public function removeAward(Award $award): void
    {
        $this->awards->removeElement($award);
    }

    /**
     * @return Collection<int, Award>
     */
    public function getAwards(): Collection
    {
        return $this->awards;
    }

    public function getSecondImage(): ?ModelImage
    {
        return $this->secondImage;
    }

    public function addImage(ProductImage $image): self
    {
        if (!$this->images->contains($image)) {
            $this->images[] = $image;
            $image->setProduct($this);
        }

        return $this;
    }

    public function removeImage(ProductImage $image): self
    {
        if ($this->images->contains($image)) {
            $this->images->removeElement($image);

            if ($image->getProduct() === $this) {
                $image->setProduct(null);
            }
        }

        return $this;
    }

    /**
     * @return Collection<int, ProductImage>
     */
    public function getImages(): Collection
    {
        return $this->images;
    }

    public function addFile(ProductFile $file): self
    {
        if (!$this->files->contains($file)) {
            $this->files[] = $file;
            $file->setProduct($this);
        }

        return $this;
    }

    public function removeFile(ProductFile $file): self
    {
        if ($this->images->contains($file)) {
            $this->images->removeElement($file);

            if ($file->getProduct() === $this) {
                $file->setProduct(null);
            }
        }

        return $this;
    }

    /**
     * @return Collection<int, ProductFile>
     */
    public function getFiles(): Collection
    {
        return $this->files;
    }

    public function addGroup(ProductPriceGroup $group): self
    {
        $this->groups[] = $group;

        return $this;
    }

    public function removeGroup(ProductPriceGroup $group): void
    {
        $this->groups->removeElement($group);
    }

    /**
     * @return Collection<int, ProductPriceGroup>
     */
    public function getGroups(): Collection
    {
        return $this->groups;
    }

    public function setSecondImage(?ModelImage $secondImage): void
    {
        if (null !== $secondImage && ($secondImage->getPath() || $secondImage->getFile())) {
            $this->secondImage = $secondImage;
        } else {
            $this->secondImage = null;
        }
    }

    public function getFullName(): string
    {
        $fullName = '';

        if ($this->getHeader()) {
            $fullName = $this->getHeader().' ';
        }

        $fullName .= $this->getName();

        if ($this->getQuality()) {
            $fullName .= ' '.$this->getQuality();
        }

        return $fullName;
    }

    public function setDegressivities(array $degressivities): void
    {
        $this->degressivities = $degressivities;
    }

    public function getDegressivities(): array
    {
        return $this->degressivities;
    }

    public function getColorGradient1(): ?string
    {
        return $this->colorGradient1;
    }

    public function setColorGradient1(string $colorGradient1): void
    {
        $this->colorGradient1 = $colorGradient1;
    }

    public function getColorGradient2(): ?string
    {
        return $this->colorGradient2;
    }

    public function setColorGradient2(string $colorGradient2): void
    {
        $this->colorGradient2 = $colorGradient2;
    }

    #[Assert\Callback]
    public function validate(ExecutionContextInterface $context): void
    {
        if (!$this->getImage()) {
            $message = 'Veuillez ajouter une image';

            $context->buildViolation($message)
                ->atPath('image.file')
                ->addViolation();
        }
    }

    public function getReferenceToken(): ?string
    {
        return $this->referenceToken;
    }

    public function setReferenceToken(string $referenceToken): void
    {
        $this->referenceToken = $referenceToken;
    }

    public function getVariantByDefault(): ProductVariant|false
    {
        $variantDefault = $this->variants->filter(function (ProductVariant $productVariant) {
            return $productVariant->isByDefault();
        })->first();
        if (\is_bool($variantDefault)) {
            AssertDomain::false($variantDefault, 'No variantDefault for '.$this->getId());
        }

        return $variantDefault;
    }
}
