<?php

declare(strict_types=1);

namespace CpCreation\VitiCore\PageBuilder\Model;

use CpCreation\VitiCore\Behavior\HasDisable;
use CpCreation\VitiCore\Behavior\HasTimestamp;
use CpCreation\VitiCore\Behavior\HasTranslatable;
use CpCreation\VitiCore\Behavior\Impl\Disable;
use CpCreation\VitiCore\Behavior\Impl\Timestamp;
use CpCreation\VitiCore\Behavior\Impl\Translatable;
use CpCreation\VitiCore\PageBuilder\Repository\PageRepository;
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;

#[ORM\Entity(repositoryClass: PageRepository::class)]
#[ORM\Table(name: 'page_builder_page')]
#[ORM\HasLifecycleCallbacks]
class Page implements HasDisable, HasTimestamp, HasTranslatable
{
    public const CODE_HOMEPAGE = 'homepage';
    public const ROUTE_MODE_CMS = 'cms';
    public const ROUTE_MODE_SYSTEM = 'system';
    public const TEMPLATE_DEFAULT = 'default';

    use Disable;
    use Timestamp;
    use Translatable;

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

    #[ORM\Column(type: 'string', length: 64, unique: true)]
    #[Assert\NotBlank]
    private string $code;

    #[ORM\Column(type: 'string', length: 255)]
    #[Assert\NotBlank]
    private string $name;

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

    #[ORM\Column(type: 'string', length: 20, options: ['default' => self::ROUTE_MODE_CMS])]
    #[Assert\Choice(choices: [self::ROUTE_MODE_CMS, self::ROUTE_MODE_SYSTEM])]
    private string $routeMode = self::ROUTE_MODE_CMS;

    #[ORM\Column(type: 'string', length: 64, options: ['default' => self::TEMPLATE_DEFAULT])]
    private string $template = self::TEMPLATE_DEFAULT;

    #[ORM\Column(type: 'json', nullable: true)]
    private array $settings = [];

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

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

    #[ORM\Column(type: 'boolean', options: ['default' => true])]
    private bool $seoIndexable = true;

    /**
     * @var Collection<int, PageSection>
     */
    #[ORM\OneToMany(
        targetEntity: PageSection::class,
        mappedBy: 'page',
        cascade: ['all'],
        orphanRemoval: true
    )]
    #[ORM\OrderBy(['position' => 'ASC', 'createdAt' => 'ASC'])]
    private Collection $sections;

    public function __construct(string $code = self::CODE_HOMEPAGE, string $name = 'Page d’accueil')
    {
        $this->id = Uuid::uuid4();
        $this->code = trim($code);
        $this->name = trim($name);
        $this->disabled = false;
        $this->sections = new ArrayCollection();
    }

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

    public function getCode(): string
    {
        return $this->code;
    }

    public function setCode(string $code): void
    {
        $this->code = trim($code);
    }

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

    public function setName(string $name): void
    {
        $this->name = trim($name);
    }

    public function getRouteName(): ?string
    {
        return $this->routeName;
    }

    public function setRouteName(?string $routeName): void
    {
        $routeName = null !== $routeName ? trim($routeName) : null;
        $this->routeName = '' !== $routeName ? $routeName : null;
    }

    public function getRouteMode(): string
    {
        return $this->routeMode;
    }

    public function setRouteMode(string $routeMode): void
    {
        $this->routeMode = $routeMode;
    }

    public function isSystem(): bool
    {
        return self::ROUTE_MODE_SYSTEM === $this->routeMode;
    }

    public function setSystem(bool $system): void
    {
        $this->routeMode = $system ? self::ROUTE_MODE_SYSTEM : self::ROUTE_MODE_CMS;
    }

    public function getTemplate(): string
    {
        return $this->template;
    }

    public function setTemplate(?string $template): void
    {
        $template = trim((string) $template);
        $this->template = '' !== $template ? $template : self::TEMPLATE_DEFAULT;
    }

    public function getSettings(): array
    {
        return $this->settings;
    }

    /**
     * @param array<string, mixed>|null $settings
     */
    public function setSettings(?array $settings): void
    {
        $this->settings = array_filter($settings ?? [], static fn (mixed $value): bool => null !== $value);
    }

    public function getSetting(string $key, mixed $default = null): mixed
    {
        return $this->settings[$key] ?? $default;
    }

    public function setSetting(string $key, mixed $value): void
    {
        if (null === $value || '' === $value || [] === $value) {
            unset($this->settings[$key]);

            return;
        }

        $this->settings[$key] = $value;
    }

    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;
    }

    public function isSeoIndexable(): bool
    {
        return $this->seoIndexable;
    }

    public function setSeoIndexable(bool $seoIndexable): void
    {
        $this->seoIndexable = $seoIndexable;
    }

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

        $referenceDate ??= new \DateTimeImmutable();

        if ($this->publishedAt instanceof \DateTimeInterface && $this->publishedAt > $referenceDate) {
            return false;
        }

        if ($this->unPublishedAt instanceof \DateTimeInterface && $this->unPublishedAt < $referenceDate) {
            return false;
        }

        return true;
    }

    /**
     * @return Collection<int, PageSection>
     */
    public function getSections(): Collection
    {
        return $this->sections;
    }

    public function addSection(PageSection $section): void
    {
        if ($this->sections->contains($section)) {
            return;
        }

        $section->setPage($this);
        $this->sections->add($section);
    }

    public function removeSection(PageSection $section): void
    {
        if (!$this->sections->removeElement($section)) {
            return;
        }

        $section->clearPage();
    }
}
