<?php

declare(strict_types=1);

namespace App\Framework\Queue\Entities;

use App\Framework\Database\Attributes\Column;
use App\Framework\Database\Attributes\Entity;
use App\Framework\Database\Attributes\Id;
use App\Framework\Queue\ValueObjects\ChainExecutionMode;
use App\Framework\Queue\ValueObjects\JobChain;
use App\Framework\Ulid\Ulid;

/**
 * Entity representing a job chain entry in the database
 */
#[Entity(table: 'job_chains')]
final readonly class JobChainEntry
{
    public function __construct(
        #[Id]
        #[Column(name: 'id', type: 'string', length: 26)]
        public string $id,
        #[Column(name: 'chain_id', type: 'string', length: 26)]
        public string $chainId,
        #[Column(name: 'name', type: 'string', length: 255)]
        public string $name,
        #[Column(name: 'job_ids', type: 'json')]
        public string $jobIds,
        #[Column(name: 'execution_mode', type: 'string', length: 20)]
        public string $executionMode,
        #[Column(name: 'created_at', type: 'timestamp')]
        public string $createdAt,
        #[Column(name: 'updated_at', type: 'timestamp')]
        public string $updatedAt,
        #[Column(name: 'stop_on_failure', type: 'boolean', default: true)]
        public bool $stopOnFailure = true,
        #[Column(name: 'metadata', type: 'json', nullable: true)]
        public ?string $metadata = null,
        #[Column(name: 'status', type: 'string', length: 20, default: 'pending')]
        public string $status = 'pending',
        #[Column(name: 'started_at', type: 'timestamp', nullable: true)]
        public ?string $startedAt = null,
        #[Column(name: 'completed_at', type: 'timestamp', nullable: true)]
        public ?string $completedAt = null
    ) {
    }

    public static function fromJobChain(JobChain $jobChain): self
    {
        $now = date('Y-m-d H:i:s');

        return new self(
            id: Ulid::generate(),
            chainId: $jobChain->chainId,
            name: $jobChain->name,
            jobIds: json_encode($jobChain->jobIds),
            executionMode: $jobChain->executionMode->value,
            createdAt: $now,
            updatedAt: $now,
            stopOnFailure: $jobChain->stopOnFailure,
            metadata: $jobChain->metadata ? json_encode($jobChain->metadata) : null,
            status: 'pending',
            startedAt: null,
            completedAt: null
        );
    }

    public function getJobChain(): JobChain
    {
        $jobIds = json_decode($this->jobIds, true);
        $metadata = $this->metadata ? json_decode($this->metadata, true) : null;

        return new JobChain(
            chainId: $this->chainId,
            name: $this->name,
            jobIds: $jobIds,
            executionMode: ChainExecutionMode::from($this->executionMode),
            stopOnFailure: $this->stopOnFailure,
            metadata: $metadata
        );
    }

    public function getJobIdsArray(): array
    {
        return json_decode($this->jobIds, true);
    }

    public function getMetadataArray(): ?array
    {
        return $this->metadata ? json_decode($this->metadata, true) : null;
    }

    public function getExecutionMode(): ChainExecutionMode
    {
        return ChainExecutionMode::from($this->executionMode);
    }

    public function markAsStarted(): self
    {
        return new self(
            id: $this->id,
            chainId: $this->chainId,
            name: $this->name,
            jobIds: $this->jobIds,
            executionMode: $this->executionMode,
            createdAt: $this->createdAt,
            updatedAt: date('Y-m-d H:i:s'),
            stopOnFailure: $this->stopOnFailure,
            metadata: $this->metadata,
            status: 'running',
            startedAt: date('Y-m-d H:i:s'),
            completedAt: $this->completedAt
        );
    }

    public function markAsCompleted(): self
    {
        return new self(
            id: $this->id,
            chainId: $this->chainId,
            name: $this->name,
            jobIds: $this->jobIds,
            executionMode: $this->executionMode,
            createdAt: $this->createdAt,
            updatedAt: date('Y-m-d H:i:s'),
            stopOnFailure: $this->stopOnFailure,
            metadata: $this->metadata,
            status: 'completed',
            startedAt: $this->startedAt,
            completedAt: date('Y-m-d H:i:s')
        );
    }

    public function markAsFailed(): self
    {
        return new self(
            id: $this->id,
            chainId: $this->chainId,
            name: $this->name,
            jobIds: $this->jobIds,
            executionMode: $this->executionMode,
            createdAt: $this->createdAt,
            updatedAt: date('Y-m-d H:i:s'),
            stopOnFailure: $this->stopOnFailure,
            metadata: $this->metadata,
            status: 'failed',
            startedAt: $this->startedAt,
            completedAt: date('Y-m-d H:i:s')
        );
    }

    public function isPending(): bool
    {
        return $this->status === 'pending';
    }

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

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

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

    public function toArray(): array
    {
        return [
            'id' => $this->id,
            'chain_id' => $this->chainId,
            'name' => $this->name,
            'job_ids' => $this->getJobIdsArray(),
            'execution_mode' => $this->executionMode,
            'stop_on_failure' => $this->stopOnFailure,
            'metadata' => $this->getMetadataArray(),
            'status' => $this->status,
            'started_at' => $this->startedAt,
            'completed_at' => $this->completedAt,
            'created_at' => $this->createdAt,
            'updated_at' => $this->updatedAt,
        ];
    }
}
