<?php

declare(strict_types=1);

namespace CpCreation\VitiCore\Shop\Model;

use CpCreation\VitiCore\Product\Model\ProductPriceGroup;
use CpCreation\VitiCore\Behavior\HasDisable;
use CpCreation\VitiCore\Behavior\HasImage;
use CpCreation\VitiCore\Behavior\HasSortable;
use CpCreation\VitiCore\Behavior\HasTimestamp;
use CpCreation\VitiCore\Behavior\HasTranslatable;
use CpCreation\VitiCore\Behavior\Impl\Disable;
use CpCreation\VitiCore\Behavior\Impl\Image;
use CpCreation\VitiCore\Behavior\Impl\Sortable;
use CpCreation\VitiCore\Behavior\Impl\Timestamp;
use CpCreation\VitiCore\Behavior\Impl\Translatable;
use CpCreation\VitiCore\Shop\Repository\InformationBlockRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;

#[ORM\Entity(repositoryClass: InformationBlockRepository::class)]
#[ORM\Table(name: 'shop_information_block')]
#[ORM\HasLifecycleCallbacks]
class InformationBlock implements HasDisable, HasImage, HasSortable, HasTimestamp, HasTranslatable
{
    public const ACTION_NONE = 'none';
    public const ACTION_MODAL = 'modal';
    public const ACTION_LINK = 'link';
    public const MODAL_TEMPLATE_NONE = 'none';
    public const MODAL_TEMPLATE_SHIPPING_FEES = 'shipping_fees';
    public const ZONE_TOP = 'top';
    public const ZONE_BOTTOM = 'bottom';

    use Disable;
    use Image;
    use Sortable;
    use Timestamp;
    use Translatable;

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

    #[ORM\Column(type: 'string', length: 20, options: ['default' => self::ACTION_NONE])]
    #[Assert\Choice(choices: [self::ACTION_NONE, self::ACTION_MODAL, self::ACTION_LINK])]
    private string $actionType = self::ACTION_NONE;

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

    #[ORM\Column(type: 'string', length: 50, options: ['default' => self::MODAL_TEMPLATE_NONE])]
    #[Assert\Choice(choices: [self::MODAL_TEMPLATE_NONE, self::MODAL_TEMPLATE_SHIPPING_FEES])]
    private string $modalTemplate = self::MODAL_TEMPLATE_NONE;

    #[ORM\Column(type: 'string', length: 10, options: ['default' => self::ZONE_TOP])]
    #[Assert\Choice(choices: [self::ZONE_TOP, self::ZONE_BOTTOM])]
    private string $displayZone = self::ZONE_TOP;

    #[ORM\Column(type: 'datetime', nullable: true)]
    private ?\DateTime $publishedAt = null;

    #[ORM\Column(type: 'datetime', nullable: true)]
    private ?\DateTime $unPublishedAt = null;

    /**
     * @var Collection<int, ProductPriceGroup>
     */
    #[ORM\ManyToMany(targetEntity: ProductPriceGroup::class)]
    #[ORM\JoinTable(name: 'shop_information_block_group_price')]
    #[ORM\OrderBy(['name' => 'ASC'])]
    private Collection $groups;

    public function __construct()
    {
        $this->id = Uuid::uuid4();
        $this->disabled = false;
        $this->position = 0;
        $this->groups = new ArrayCollection();
    }

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

    public function getActionType(): string
    {
        return $this->actionType;
    }

    public function setActionType(string $actionType): void
    {
        $this->actionType = $actionType;
    }

    public function getButtonUrl(): ?string
    {
        return $this->buttonUrl;
    }

    public function setButtonUrl(?string $buttonUrl): void
    {
        $this->buttonUrl = null !== $buttonUrl ? trim($buttonUrl) : null;
    }

    public function getModalTemplate(): string
    {
        return $this->modalTemplate;
    }

    public function setModalTemplate(string $modalTemplate): void
    {
        $this->modalTemplate = $modalTemplate;
    }

    public function isModalAction(): bool
    {
        return self::ACTION_MODAL === $this->actionType;
    }

    public function isLinkAction(): bool
    {
        return self::ACTION_LINK === $this->actionType;
    }

    public function hasModalTemplate(): bool
    {
        return self::MODAL_TEMPLATE_NONE !== $this->modalTemplate;
    }

    public function hasModalContent(): bool
    {
        if ($this->hasModalTemplate()) {
            return true;
        }

        return '' !== trim((string) $this->getModalDescription());
    }

    public function getDisplayZone(): string
    {
        return $this->displayZone;
    }

    public function setDisplayZone(string $displayZone): void
    {
        $this->displayZone = $displayZone;
    }

    public function getPublishedAt(): ?\DateTime
    {
        return $this->publishedAt;
    }

    public function setPublishedAt(?\DateTime $publishedAt): void
    {
        $this->publishedAt = $publishedAt;
    }

    public function getUnPublishedAt(): ?\DateTime
    {
        return $this->unPublishedAt;
    }

    public function setUnPublishedAt(?\DateTime $unPublishedAt): void
    {
        $this->unPublishedAt = $unPublishedAt;
    }

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

    public function addGroup(ProductPriceGroup $group): void
    {
        if (!$this->groups->contains($group)) {
            $this->groups->add($group);
        }
    }

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

    public function isAvailableForGroup(?string $groupId): bool
    {
        if ($this->groups->isEmpty()) {
            return true;
        }

        if (null === $groupId || '' === $groupId) {
            return false;
        }

        foreach ($this->groups as $group) {
            if ($group->getId()->toString() === $groupId) {
                return true;
            }
        }

        return false;
    }

    public function isCurrentlyPublished(?\DateTimeInterface $referenceDate = null): bool
    {
        if ($this->isDisabled()) {
            return false;
        }

        $referenceDate ??= new \DateTimeImmutable('now');

        if (null !== $this->publishedAt && $this->publishedAt > $referenceDate) {
            return false;
        }

        if (null !== $this->unPublishedAt && $this->unPublishedAt < $referenceDate) {
            return false;
        }

        return true;
    }

    #[Assert\Callback]
    public function validateActionConfiguration(ExecutionContextInterface $context): void
    {
        if ($this->isLinkAction() && '' === trim((string) $this->buttonUrl)) {
            $context->buildViolation('Le lien du bouton est obligatoire lorsque le bouton ouvre une URL.')
                ->atPath('buttonUrl')
                ->addViolation();
        }

        if ($this->isModalAction() && !$this->hasModalContent()) {
            $context->buildViolation('Renseignez un template de modale ou une description de modale lorsque le bouton ouvre une modale.')
                ->atPath('modalDescription')
                ->addViolation();
        }

        if (
            null !== $this->publishedAt
            && null !== $this->unPublishedAt
            && $this->unPublishedAt <= $this->publishedAt
        ) {
            $context->buildViolation('La date de dépublication doit être postérieure à la date de publication.')
                ->atPath('unPublishedAt')
                ->addViolation();
        }
    }
}
