<?php

declare(strict_types=1);

namespace App\Framework\Queue\MachineLearning\ValueObjects;

use App\Framework\Queue\ValueObjects\JobMetrics;
use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\Core\ValueObjects\Duration;

/**
 * Job Execution Context - Metadata for Job Analysis
 *
 * Immutable value object containing execution metadata
 * for job anomaly detection analysis.
 *
 * Features:
 * - Execution timing and performance metrics
 * - Retry and failure information
 * - Queue depth and workload context
 * - Memory and resource usage patterns
 *
 * Used by: JobFeatureExtractor for behavioral analysis
 */
final readonly class JobExecutionContext
{
    /**
     * @param string $jobId Job identifier
     * @param string $queueName Queue name
     * @param string $status Current job status (pending, running, completed, failed)
     * @param int $attempts Number of execution attempts
     * @param int $maxAttempts Maximum allowed attempts
     * @param float $executionTimeMs Execution time in milliseconds
     * @param int $memoryUsageBytes Memory usage in bytes
     * @param ?string $errorMessage Error message if failed
     * @param Timestamp $createdAt Job creation timestamp
     * @param ?Timestamp $startedAt Job start timestamp
     * @param ?Timestamp $completedAt Job completion timestamp
     * @param ?Timestamp $failedAt Job failure timestamp
     * @param int $queueDepth Current queue depth (jobs waiting)
     * @param int $payloadSizeBytes Estimated job payload size
     * @param array $metadata Additional metadata
     */
    public function __construct(
        public string $jobId,
        public string $queueName,
        public string $status,
        public int $attempts,
        public int $maxAttempts,
        public float $executionTimeMs,
        public int $memoryUsageBytes,
        public ?string $errorMessage,
        public Timestamp $createdAt,
        public ?Timestamp $startedAt,
        public ?Timestamp $completedAt,
        public ?Timestamp $failedAt,
        public int $queueDepth = 0,
        public int $payloadSizeBytes = 0,
        public array $metadata = []
    ) {}

    /**
     * Create from JobMetrics value object
     */
    public static function fromJobMetrics(
        JobMetrics $metrics,
        int $queueDepth = 0,
        int $payloadSizeBytes = 0
    ): self {
        return new self(
            jobId: $metrics->jobId,
            queueName: $metrics->queueName,
            status: $metrics->status,
            attempts: $metrics->attempts,
            maxAttempts: $metrics->maxAttempts,
            executionTimeMs: $metrics->executionTimeMs,
            memoryUsageBytes: $metrics->memoryUsageBytes,
            errorMessage: $metrics->errorMessage,
            createdAt: Timestamp::fromString($metrics->createdAt),
            startedAt: $metrics->startedAt ? Timestamp::fromString($metrics->startedAt) : null,
            completedAt: $metrics->completedAt ? Timestamp::fromString($metrics->completedAt) : null,
            failedAt: $metrics->failedAt ? Timestamp::fromString($metrics->failedAt) : null,
            queueDepth: $queueDepth,
            payloadSizeBytes: $payloadSizeBytes,
            metadata: $metrics->metadata
        );
    }

    /**
     * Check if job completed successfully
     */
    public function isCompleted(): bool
    {
        return $this->status === 'completed';
    }

    /**
     * Check if job failed
     */
    public function isFailed(): bool
    {
        return $this->status === 'failed';
    }

    /**
     * Check if job is currently running
     */
    public function isRunning(): bool
    {
        return $this->status === 'running';
    }

    /**
     * Check if job is pending
     */
    public function isPending(): bool
    {
        return $this->status === 'pending';
    }

    /**
     * Check if job exceeded retry attempts
     */
    public function hasExceededRetries(): bool
    {
        return $this->attempts >= $this->maxAttempts;
    }

    /**
     * Get retry rate (attempts / maxAttempts)
     */
    public function getRetryRate(): float
    {
        if ($this->maxAttempts === 0) {
            return 0.0;
        }

        return min(1.0, $this->attempts / $this->maxAttempts);
    }

    /**
     * Get execution duration
     */
    public function getExecutionDuration(): ?Duration
    {
        if ($this->startedAt === null) {
            return null;
        }

        $endTimestamp = $this->completedAt ?? $this->failedAt ?? Timestamp::now();

        $durationSeconds = $endTimestamp->toFloat() - $this->startedAt->toFloat();

        return Duration::fromSeconds((int) $durationSeconds);
    }

    /**
     * Get queue time (time from creation to start)
     */
    public function getQueueTime(): ?Duration
    {
        if ($this->startedAt === null) {
            return null;
        }

        $queueTimeSeconds = $this->startedAt->toFloat() - $this->createdAt->toFloat();

        return Duration::fromSeconds((int) $queueTimeSeconds);
    }

    /**
     * Get execution time in seconds
     */
    public function getExecutionTimeSeconds(): float
    {
        return $this->executionTimeMs / 1000.0;
    }

    /**
     * Get memory usage in megabytes
     */
    public function getMemoryUsageMB(): float
    {
        return $this->memoryUsageBytes / (1024 * 1024);
    }

    /**
     * Get payload size in kilobytes
     */
    public function getPayloadSizeKB(): float
    {
        return $this->payloadSizeBytes / 1024;
    }

    /**
     * Check if job has error message
     */
    public function hasError(): bool
    {
        return $this->errorMessage !== null;
    }

    /**
     * Get metadata value by key
     */
    public function getMetadata(string $key, mixed $default = null): mixed
    {
        return $this->metadata[$key] ?? $default;
    }

    /**
     * Convert to array for serialization
     */
    public function toArray(): array
    {
        return [
            '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->format('Y-m-d H:i:s'),
            'started_at' => $this->startedAt?->format('Y-m-d H:i:s'),
            'completed_at' => $this->completedAt?->format('Y-m-d H:i:s'),
            'failed_at' => $this->failedAt?->format('Y-m-d H:i:s'),
            'queue_depth' => $this->queueDepth,
            'payload_size_bytes' => $this->payloadSizeBytes,
            'payload_size_kb' => $this->getPayloadSizeKB(),
            'retry_rate' => $this->getRetryRate(),
            'execution_duration_seconds' => $this->getExecutionDuration()?->toSeconds(),
            'queue_time_seconds' => $this->getQueueTime()?->toSeconds(),
            'metadata' => $this->metadata,
        ];
    }

    /**
     * Create debugging representation
     */
    public function __toString(): string
    {
        return sprintf(
            'JobExecution[%s, queue=%s, status=%s, attempts=%d/%d, time=%.2fms, memory=%.2fMB]',
            $this->jobId,
            $this->queueName,
            $this->status,
            $this->attempts,
            $this->maxAttempts,
            $this->executionTimeMs,
            $this->getMemoryUsageMB()
        );
    }
}
