<?php

declare(strict_types=1);

namespace CpCreation\VitiCore\Versioning\Model;

use CpCreation\VitiCore\Versioning\Repository\BusinessVersionRepository;
use Doctrine\ORM\Mapping as ORM;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;

#[ORM\Entity(repositoryClass: BusinessVersionRepository::class)]
#[ORM\Table(name: 'business_version')]
#[ORM\UniqueConstraint(name: 'business_version_object_version_unique', columns: ['object_class', 'object_id', 'scope', 'version'])]
#[ORM\Index(name: 'business_version_object_idx', columns: ['object_class', 'object_id', 'scope'])]
#[ORM\Index(name: 'business_version_status_idx', columns: ['status'])]
class BusinessVersion
{
    public const STATUS_DRAFT = 'draft';
    public const STATUS_PUBLISHED = 'published';
    public const STATUS_ARCHIVED = 'archived';

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

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

    #[ORM\Column(name: 'object_id', type: 'string', length: 128)]
    private string $objectId;

    #[ORM\Column(type: 'string', length: 64)]
    private string $scope;

    #[ORM\Column(type: 'integer')]
    private int $version;

    #[ORM\Column(type: 'string', length: 32)]
    private string $status = self::STATUS_DRAFT;

    /**
     * @var array<string, mixed>
     */
    #[ORM\Column(type: 'json')]
    private array $snapshot = [];

    /**
     * @var array<string, mixed>
     */
    #[ORM\Column(type: 'json')]
    private array $metadata = [];

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

    #[ORM\Column(name: 'valid_from', type: 'datetime_immutable', nullable: true)]
    private ?\DateTimeImmutable $validFrom = null;

    #[ORM\Column(name: 'valid_to', type: 'datetime_immutable', nullable: true)]
    private ?\DateTimeImmutable $validTo = null;

    #[ORM\Column(name: 'published_at', type: 'datetime_immutable', nullable: true)]
    private ?\DateTimeImmutable $publishedAt = null;

    #[ORM\Column(name: 'created_at', type: 'datetime_immutable')]
    private \DateTimeImmutable $createdAt;

    public function __construct(string $objectClass, string $objectId, int $version, array $snapshot, string $scope = 'default')
    {
        $this->id = Uuid::uuid4();
        $this->objectClass = $objectClass;
        $this->objectId = $objectId;
        $this->version = $version;
        $this->snapshot = $snapshot;
        $this->scope = $scope;
        $this->createdAt = new \DateTimeImmutable();
    }

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

    public function getObjectClass(): string
    {
        return $this->objectClass;
    }

    public function getObjectId(): string
    {
        return $this->objectId;
    }

    public function getScope(): string
    {
        return $this->scope;
    }

    public function getVersion(): int
    {
        return $this->version;
    }

    public function getStatus(): string
    {
        return $this->status;
    }

    public function setStatus(string $status): void
    {
        if (!\in_array($status, [self::STATUS_DRAFT, self::STATUS_PUBLISHED, self::STATUS_ARCHIVED], true)) {
            throw new \InvalidArgumentException(sprintf('Invalid business version status "%s".', $status));
        }

        $this->status = $status;
    }

    /**
     * @return array<string, mixed>
     */
    public function getSnapshot(): array
    {
        return $this->snapshot;
    }

    /**
     * @param array<string, mixed> $snapshot
     */
    public function setSnapshot(array $snapshot): void
    {
        $this->snapshot = $snapshot;
    }

    /**
     * @return array<string, mixed>
     */
    public function getMetadata(): array
    {
        return $this->metadata;
    }

    /**
     * @param array<string, mixed> $metadata
     */
    public function setMetadata(array $metadata): void
    {
        $this->metadata = $metadata;
    }

    public function getCreatedBy(): ?string
    {
        return $this->createdBy;
    }

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

    public function getValidFrom(): ?\DateTimeImmutable
    {
        return $this->validFrom;
    }

    public function setValidFrom(?\DateTimeImmutable $validFrom): void
    {
        $this->validFrom = $validFrom;
    }

    public function getValidTo(): ?\DateTimeImmutable
    {
        return $this->validTo;
    }

    public function setValidTo(?\DateTimeImmutable $validTo): void
    {
        $this->validTo = $validTo;
    }

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

    public function publish(?\DateTimeImmutable $publishedAt = null): void
    {
        $this->status = self::STATUS_PUBLISHED;
        $this->publishedAt = $publishedAt ?? new \DateTimeImmutable();
    }

    public function archive(?\DateTimeImmutable $validTo = null): void
    {
        $this->status = self::STATUS_ARCHIVED;
        $this->validTo = $validTo ?? new \DateTimeImmutable();
    }

    public function getCreatedAt(): \DateTimeImmutable
    {
        return $this->createdAt;
    }
}
