<?php

declare(strict_types=1);

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Uid\Uuid;

#[ORM\Entity]
#[ORM\Table(name: 'tag_rebuild_job')]
#[ORM\Index(name: 'idx_tag_rebuild_job_status', columns: ['status'])]
#[ORM\Index(name: 'idx_tag_rebuild_job_created_at', columns: ['created_at'])]
class TagRebuildJob
{
    public const STATUS_QUEUED = 'QUEUED';
    public const STATUS_RUNNING = 'RUNNING';
    public const STATUS_COMPLETED = 'COMPLETED';
    public const STATUS_FAILED = 'FAILED';

    private const ERROR_MESSAGE_MAX_LENGTH = 4000;

    #[ORM\Id]
    #[ORM\Column(type: 'uuid', unique: true)]
    private Uuid $id;

    #[ORM\Column(length: 16)]
    private string $status = self::STATUS_QUEUED;

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

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

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

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

    public function __construct()
    {
        $this->id = Uuid::v4();
        $this->createdAt = new \DateTimeImmutable();
        $this->status = self::STATUS_QUEUED;
    }

    /**
     * @return list<string>
     */
    public static function statuses(): array
    {
        return [
            self::STATUS_QUEUED,
            self::STATUS_RUNNING,
            self::STATUS_COMPLETED,
            self::STATUS_FAILED,
        ];
    }

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

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

    public function isQueued(): bool
    {
        return $this->status === self::STATUS_QUEUED;
    }

    public function isRunning(): bool
    {
        return $this->status === self::STATUS_RUNNING;
    }

    public function isCompleted(): bool
    {
        return $this->status === self::STATUS_COMPLETED;
    }

    public function isFailed(): bool
    {
        return $this->status === self::STATUS_FAILED;
    }

    public function isActive(): bool
    {
        return $this->isQueued() || $this->isRunning();
    }

    public function markRunning(): void
    {
        $this->status = self::STATUS_RUNNING;
        $this->startedAt = new \DateTimeImmutable();
        $this->finishedAt = null;
        $this->errorMessage = null;
    }

    public function markCompleted(): void
    {
        if ($this->startedAt === null) {
            $this->startedAt = new \DateTimeImmutable();
        }

        $this->status = self::STATUS_COMPLETED;
        $this->finishedAt = new \DateTimeImmutable();
        $this->errorMessage = null;
    }

    public function markFailed(string $message): void
    {
        if ($this->startedAt === null) {
            $this->startedAt = new \DateTimeImmutable();
        }

        $this->status = self::STATUS_FAILED;
        $this->finishedAt = new \DateTimeImmutable();
        $this->errorMessage = $this->normalizeErrorMessage($message);
    }

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

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

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

    public function getErrorMessage(): ?string
    {
        return $this->errorMessage;
    }

    private function normalizeErrorMessage(string $message): ?string
    {
        $message = trim($message);

        if ($message === '') {
            return 'Unknown tag rebuild failure.';
        }

        if (mb_strlen($message) > self::ERROR_MESSAGE_MAX_LENGTH) {
            $message = mb_substr($message, 0, self::ERROR_MESSAGE_MAX_LENGTH);
        }

        return $message;
    }
}