<?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\JobMetrics;
use App\Framework\Ulid\Ulid;

#[Entity(table: 'job_metrics')]
final readonly class JobMetricsEntry
{
    public function __construct(
        #[Id]
        #[Column(name: 'id', type: 'string', length: 26)]
        public string $id,
        #[Column(name: 'job_id', type: 'string', length: 26)]
        public string $jobId,
        #[Column(name: 'queue_name', type: 'string', length: 100)]
        public string $queueName,
        #[Column(name: 'status', type: 'string', length: 20)]
        public string $status,
        #[Column(name: 'attempts', type: 'integer')]
        public int $attempts,
        #[Column(name: 'max_attempts', type: 'integer')]
        public int $maxAttempts,
        #[Column(name: 'execution_time_ms', type: 'float')]
        public float $executionTimeMs,
        #[Column(name: 'memory_usage_bytes', type: 'integer')]
        public int $memoryUsageBytes,
        #[Column(name: 'created_at', type: 'timestamp')]
        public string $createdAt,
        #[Column(name: 'updated_at', type: 'timestamp')]
        public string $updatedAt,
        #[Column(name: 'error_message', type: 'text', nullable: true)]
        public ?string $errorMessage = null,
        #[Column(name: 'started_at', type: 'timestamp', nullable: true)]
        public ?string $startedAt = null,
        #[Column(name: 'completed_at', type: 'timestamp', nullable: true)]
        public ?string $completedAt = null,
        #[Column(name: 'failed_at', type: 'timestamp', nullable: true)]
        public ?string $failedAt = null,
        #[Column(name: 'metadata', type: 'json', nullable: true)]
        public ?string $metadata = null
    ) {
    }

    public static function fromJobMetrics(JobMetrics $metrics): self
    {
        $now = date('Y-m-d H:i:s');

        return new self(
            id: Ulid::generate(),
            jobId: $metrics->jobId,
            queueName: $metrics->queueName,
            status: $metrics->status,
            attempts: $metrics->attempts,
            maxAttempts: $metrics->maxAttempts,
            executionTimeMs: $metrics->executionTimeMs,
            memoryUsageBytes: $metrics->memoryUsageBytes,
            createdAt: $metrics->createdAt,
            updatedAt: $now,
            errorMessage: $metrics->errorMessage,
            startedAt: $metrics->startedAt,
            completedAt: $metrics->completedAt,
            failedAt: $metrics->failedAt,
            metadata: ! empty($metrics->metadata) ? json_encode($metrics->metadata) : null
        );
    }

    public function getJobMetrics(): JobMetrics
    {
        return new JobMetrics(
            jobId: $this->jobId,
            queueName: $this->queueName,
            status: $this->status,
            attempts: $this->attempts,
            maxAttempts: $this->maxAttempts,
            executionTimeMs: $this->executionTimeMs,
            memoryUsageBytes: $this->memoryUsageBytes,
            errorMessage: $this->errorMessage,
            createdAt: $this->createdAt,
            startedAt: $this->startedAt,
            completedAt: $this->completedAt,
            failedAt: $this->failedAt,
            metadata: $this->metadata ? json_decode($this->metadata, true) : []
        );
    }

    public function updateWithMetrics(JobMetrics $metrics): self
    {
        return new self(
            id: $this->id,
            jobId: $this->jobId,
            queueName: $this->queueName,
            status: $metrics->status,
            attempts: $metrics->attempts,
            maxAttempts: $metrics->maxAttempts,
            executionTimeMs: $metrics->executionTimeMs,
            memoryUsageBytes: $metrics->memoryUsageBytes,
            createdAt: $this->createdAt,
            updatedAt: date('Y-m-d H:i:s'),
            errorMessage: $metrics->errorMessage,
            startedAt: $metrics->startedAt,
            completedAt: $metrics->completedAt,
            failedAt: $metrics->failedAt,
            metadata: ! empty($metrics->metadata) ? json_encode($metrics->metadata) : $this->metadata
        );
    }

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

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

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

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

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

    public function getExecutionTimeSeconds(): float
    {
        return $this->executionTimeMs / 1000.0;
    }

    public function getMemoryUsageMB(): float
    {
        return $this->memoryUsageBytes / (1024 * 1024);
    }

    public function toArray(): array
    {
        return [
            'id' => $this->id,
            'job_id' => $this->jobId,
            'queue_name' => $this->queueName,
            'status' => $this->status,
            'attempts' => $this->attempts,
            'max_attempts' => $this->maxAttempts,
            'execution_time_ms' => $this->executionTimeMs,
            'execution_time_seconds' => $this->getExecutionTimeSeconds(),
            'memory_usage_bytes' => $this->memoryUsageBytes,
            'memory_usage_mb' => $this->getMemoryUsageMB(),
            'error_message' => $this->errorMessage,
            'created_at' => $this->createdAt,
            'updated_at' => $this->updatedAt,
            'started_at' => $this->startedAt,
            'completed_at' => $this->completedAt,
            'failed_at' => $this->failedAt,
            'metadata' => $this->getMetadataArray(),
        ];
    }
}
