<?php

declare(strict_types=1);

namespace App\Framework\Queue\ValueObjects;

use App\Framework\Core\ValueObjects\Percentage;

final readonly class QueueMetrics
{
    public function __construct(
        public string $queueName,
        public int $totalJobs,
        public int $pendingJobs,
        public int $runningJobs,
        public int $completedJobs,
        public int $failedJobs,
        public int $deadLetterJobs,
        public float $averageExecutionTimeMs,
        public float $averageMemoryUsageMB,
        public float $throughputJobsPerHour,
        public Percentage $successRate,
        public string $measuredAt,
        public array $additionalMetrics = []
    ) {
    }

    public static function calculate(
        string $queueName,
        array $jobMetrics,
        string $timeWindow = '1 hour'
    ): self {
        $total = count($jobMetrics);
        $pending = 0;
        $running = 0;
        $completed = 0;
        $failed = 0;
        $deadLetter = 0;

        $totalExecutionTime = 0.0;
        $totalMemoryUsage = 0.0;
        $executionCount = 0;

        $windowStart = strtotime("-{$timeWindow}");
        $jobsInWindow = 0;

        foreach ($jobMetrics as $metrics) {
            match($metrics->status) {
                'pending' => $pending++,
                'running' => $running++,
                'completed' => $completed++,
                'failed' => $failed++,
                'dead_letter' => $deadLetter++,
                default => null
            };

            if ($metrics->executionTimeMs > 0) {
                $totalExecutionTime += $metrics->executionTimeMs;
                $totalMemoryUsage += $metrics->getMemoryUsageMB();
                $executionCount++;
            }

            if (strtotime($metrics->createdAt) >= $windowStart) {
                $jobsInWindow++;
            }
        }

        $avgExecutionTime = $executionCount > 0 ? $totalExecutionTime / $executionCount : 0.0;
        $avgMemoryUsage = $executionCount > 0 ? $totalMemoryUsage / $executionCount : 0.0;
        $throughput = $jobsInWindow / (strtotime($timeWindow) / 3600);

        $successRate = $total > 0 ?
            Percentage::from(($completed / $total) * 100) :
            Percentage::from(100.0);

        return new self(
            queueName: $queueName,
            totalJobs: $total,
            pendingJobs: $pending,
            runningJobs: $running,
            completedJobs: $completed,
            failedJobs: $failed,
            deadLetterJobs: $deadLetter,
            averageExecutionTimeMs: $avgExecutionTime,
            averageMemoryUsageMB: $avgMemoryUsage,
            throughputJobsPerHour: $throughput,
            successRate: $successRate,
            measuredAt: date('Y-m-d H:i:s'),
            additionalMetrics: []
        );
    }

    public function withAdditionalMetrics(array $metrics): self
    {
        return new self(
            queueName: $this->queueName,
            totalJobs: $this->totalJobs,
            pendingJobs: $this->pendingJobs,
            runningJobs: $this->runningJobs,
            completedJobs: $this->completedJobs,
            failedJobs: $this->failedJobs,
            deadLetterJobs: $this->deadLetterJobs,
            averageExecutionTimeMs: $this->averageExecutionTimeMs,
            averageMemoryUsageMB: $this->averageMemoryUsageMB,
            throughputJobsPerHour: $this->throughputJobsPerHour,
            successRate: $this->successRate,
            measuredAt: $this->measuredAt,
            additionalMetrics: array_merge($this->additionalMetrics, $metrics)
        );
    }

    public function getFailureRate(): Percentage
    {
        if ($this->totalJobs === 0) {
            return Percentage::from(0.0);
        }

        return Percentage::from(($this->failedJobs / $this->totalJobs) * 100);
    }

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

    public function getHealthScore(): Percentage
    {
        // Composite health score based on multiple factors
        $successWeight = 40; // Success rate weight
        $throughputWeight = 30; // Throughput weight
        $performanceWeight = 20; // Performance weight
        $stabilityWeight = 10; // Stability weight

        // Success rate score (0-100)
        $successScore = $this->successRate->getValue();

        // Throughput score (normalized, assuming 100 jobs/hour is excellent)
        $throughputScore = min(100, ($this->throughputJobsPerHour / 100) * 100);

        // Performance score (inverse of execution time, assuming 1000ms is baseline)
        $performanceScore = $this->averageExecutionTimeMs > 0 ?
            max(0, 100 - (($this->averageExecutionTimeMs / 1000) * 10)) : 100;

        // Stability score (low pending/running job ratio)
        $activeJobs = $this->pendingJobs + $this->runningJobs;
        $stabilityScore = $this->totalJobs > 0 ?
            max(0, 100 - (($activeJobs / $this->totalJobs) * 100)) : 100;

        $weightedScore = (
            ($successScore * $successWeight) +
            ($throughputScore * $throughputWeight) +
            ($performanceScore * $performanceWeight) +
            ($stabilityScore * $stabilityWeight)
        ) / 100;

        return Percentage::from($weightedScore);
    }

    public function isHealthy(): bool
    {
        return $this->getHealthScore()->getValue() >= 70.0;
    }

    public function getBottleneckIndicators(): array
    {
        $indicators = [];

        // High pending jobs
        if ($this->pendingJobs > ($this->totalJobs * 0.3)) {
            $indicators[] = 'high_pending_jobs';
        }

        // High failure rate
        if ($this->getFailureRate()->getValue() > 10.0) {
            $indicators[] = 'high_failure_rate';
        }

        // Slow execution
        if ($this->averageExecutionTimeMs > 5000) {
            $indicators[] = 'slow_execution';
        }

        // High memory usage
        if ($this->averageMemoryUsageMB > 100) {
            $indicators[] = 'high_memory_usage';
        }

        // Low throughput
        if ($this->throughputJobsPerHour < 10) {
            $indicators[] = 'low_throughput';
        }

        return $indicators;
    }

    public function toArray(): array
    {
        return [
            'queue_name' => $this->queueName,
            'total_jobs' => $this->totalJobs,
            'pending_jobs' => $this->pendingJobs,
            'running_jobs' => $this->runningJobs,
            'completed_jobs' => $this->completedJobs,
            'failed_jobs' => $this->failedJobs,
            'dead_letter_jobs' => $this->deadLetterJobs,
            'average_execution_time_ms' => $this->averageExecutionTimeMs,
            'average_execution_time_seconds' => $this->getAverageExecutionTimeSeconds(),
            'average_memory_usage_mb' => $this->averageMemoryUsageMB,
            'throughput_jobs_per_hour' => $this->throughputJobsPerHour,
            'success_rate' => $this->successRate->getValue(),
            'failure_rate' => $this->getFailureRate()->getValue(),
            'health_score' => $this->getHealthScore()->getValue(),
            'is_healthy' => $this->isHealthy(),
            'bottleneck_indicators' => $this->getBottleneckIndicators(),
            'measured_at' => $this->measuredAt,
            'additional_metrics' => $this->additionalMetrics,
        ];
    }
}
