<?php

declare(strict_types=1);

namespace App\Framework\Queue\Entities;

use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\Database\Attributes\Column;
use App\Framework\Database\Attributes\Entity;
use App\Framework\Queue\ValueObjects\JobId;
use App\Framework\Queue\ValueObjects\JobState;
use App\Framework\Queue\ValueObjects\JobStatus;
use App\Framework\Queue\ValueObjects\QueuePriority;
use App\Framework\Queue\ValueObjects\QueueType;

/**
 * Job Index Entry Entity
 *
 * Database entity for efficient job querying and indexing
 */
#[Entity(tableName: 'job_index', idColumn: 'job_id')]
final readonly class JobIndexEntry
{
    public function __construct(
        #[Column(name: 'job_id', primary: true)]
        public JobId $jobId,
        #[Column(name: 'status')]
        public JobStatus $status,
        #[Column(name: 'queue_type')]
        public QueueType $queueType,
        #[Column(name: 'priority')]
        public QueuePriority $priority,
        #[Column(name: 'attempts')]
        public int $attempts,
        #[Column(name: 'max_attempts')]
        public int $maxAttempts,
        #[Column(name: 'created_at')]
        public Timestamp $createdAt,
        #[Column(name: 'updated_at')]
        public Timestamp $updatedAt,
        #[Column(name: 'started_at')]
        public ?Timestamp $startedAt = null,
        #[Column(name: 'completed_at')]
        public ?Timestamp $completedAt = null,
        #[Column(name: 'scheduled_for')]
        public ?Timestamp $scheduledFor = null,
        #[Column(name: 'error_message')]
        public ?string $errorMessage = null
    ) {
    }

    /**
     * Create from JobState
     */
    public static function fromJobState(JobState $jobState): self
    {
        return new self(
            jobId: $jobState->jobId,
            status: $jobState->status,
            queueType: $jobState->queueType,
            priority: $jobState->priority,
            attempts: $jobState->attempts,
            maxAttempts: $jobState->maxAttempts,
            createdAt: $jobState->createdAt,
            updatedAt: $jobState->completedAt ?? $jobState->startedAt ?? $jobState->createdAt,
            startedAt: $jobState->startedAt,
            completedAt: $jobState->completedAt,
            scheduledFor: null, // TODO: Add scheduled jobs support
            errorMessage: $jobState->errorMessage
        );
    }

    /**
     * Update with new job state
     */
    public function updateFromJobState(JobState $jobState): self
    {
        return new self(
            jobId: $this->jobId,
            status: $jobState->status,
            queueType: $this->queueType,
            priority: $this->priority,
            attempts: $jobState->attempts,
            maxAttempts: $this->maxAttempts,
            createdAt: $this->createdAt,
            updatedAt: Timestamp::now(),
            startedAt: $jobState->startedAt ?? $this->startedAt,
            completedAt: $jobState->completedAt ?? $this->completedAt,
            scheduledFor: $this->scheduledFor,
            errorMessage: $jobState->errorMessage ?? $this->errorMessage
        );
    }

    /**
     * Check if job can be retried
     */
    public function canRetry(): bool
    {
        return $this->attempts < $this->maxAttempts &&
               ($this->status === JobStatus::FAILED || $this->status === JobStatus::RETRYING);
    }

    /**
     * Check if job is in final state
     */
    public function isFinalState(): bool
    {
        return $this->status->isFinal();
    }

    /**
     * Check if job is active
     */
    public function isActive(): bool
    {
        return $this->status->isActive();
    }

    /**
     * Get job age in seconds
     */
    public function getAgeInSeconds(): int
    {
        return (int) (Timestamp::now()->toFloat() - $this->createdAt->toFloat());
    }

    /**
     * Check if job needs retry based on backoff strategy
     */
    public function needsRetry(): bool
    {
        if (! $this->canRetry()) {
            return false;
        }

        // Exponential backoff: 2^attempts minutes
        $backoffMinutes = pow(2, $this->attempts);
        $nextRetryTime = $this->updatedAt->toFloat() + ($backoffMinutes * 60);

        return Timestamp::now()->toFloat() >= $nextRetryTime;
    }

    /**
     * Convert to array for storage/serialization
     */
    public function toArray(): array
    {
        return [
            'job_id' => $this->jobId->toString(),
            'status' => $this->status->value,
            'queue_type' => $this->queueType->value,
            'priority' => $this->priority->value,
            'attempts' => $this->attempts,
            'max_attempts' => $this->maxAttempts,
            'created_at' => $this->createdAt->toFloat(),
            'updated_at' => $this->updatedAt->toFloat(),
            'started_at' => $this->startedAt?->toFloat(),
            'completed_at' => $this->completedAt?->toFloat(),
            'scheduled_for' => $this->scheduledFor?->toFloat(),
            'error_message' => $this->errorMessage,
        ];
    }
}
