<?php

declare(strict_types=1);

namespace App\Framework\Mcp\Core\ValueObjects;

/**
 * Performance Metrics Value Object
 *
 * Comprehensive performance metrics for MCP tool execution including
 * timing, memory usage, CPU usage, and execution context.
 */
final readonly class PerformanceMetrics
{
    public function __construct(
        public string $toolName,
        public string $methodName,
        public string $executionId,
        public float $startTime,
        public float $endTime,
        public float $executionTime,
        public int $memoryStart,
        public int $memoryEnd,
        public int $memoryPeak,
        public int $memoryUsage,
        public float $cpuUsage,
        public int $resultSize,
        public bool $success,
        public ?string $error,
        public array $parameters,
        public array $context
    ) {
    }

    public function toArray(): array
    {
        return [
            'tool_name' => $this->toolName,
            'method_name' => $this->methodName,
            'execution_id' => $this->executionId,
            'timing' => [
                'start_time' => $this->startTime,
                'end_time' => $this->endTime,
                'execution_time' => $this->executionTime,
                'execution_time_formatted' => $this->formatExecutionTime(),
            ],
            'memory' => [
                'start_bytes' => $this->memoryStart,
                'end_bytes' => $this->memoryEnd,
                'peak_bytes' => $this->memoryPeak,
                'usage_bytes' => $this->memoryUsage,
                'start_formatted' => $this->formatBytes($this->memoryStart),
                'end_formatted' => $this->formatBytes($this->memoryEnd),
                'peak_formatted' => $this->formatBytes($this->memoryPeak),
                'usage_formatted' => $this->formatBytes($this->memoryUsage),
            ],
            'cpu' => [
                'usage_seconds' => $this->cpuUsage,
                'usage_formatted' => $this->formatCpuUsage(),
            ],
            'result' => [
                'size_bytes' => $this->resultSize,
                'size_formatted' => $this->formatBytes($this->resultSize),
            ],
            'execution' => [
                'success' => $this->success,
                'error' => $this->error,
                'status' => $this->success ? 'success' : 'error',
            ],
            'performance_grade' => $this->getPerformanceGrade(),
            'efficiency_score' => $this->calculateEfficiencyScore(),
            'resource_intensity' => $this->getResourceIntensity(),
            'parameters' => $this->parameters,
            'context' => $this->context,
        ];
    }

    public function getPerformanceGrade(): string
    {
        $score = $this->calculateEfficiencyScore();

        return match (true) {
            $score >= 90 => 'A+',
            $score >= 85 => 'A',
            $score >= 80 => 'B+',
            $score >= 75 => 'B',
            $score >= 70 => 'C+',
            $score >= 65 => 'C',
            $score >= 60 => 'D+',
            $score >= 55 => 'D',
            default => 'F'
        };
    }

    public function calculateEfficiencyScore(): float
    {
        if (! $this->success) {
            return 0.0; // Failed executions get 0 score
        }

        // Score components (0-100 each, weighted)
        $timeScore = $this->calculateTimeScore() * 0.4;        // 40% weight
        $memoryScore = $this->calculateMemoryScore() * 0.3;    // 30% weight
        $cpuScore = $this->calculateCpuScore() * 0.2;          // 20% weight
        $resultScore = $this->calculateResultScore() * 0.1;   // 10% weight

        return round($timeScore + $memoryScore + $cpuScore + $resultScore, 2);
    }

    public function getResourceIntensity(): string
    {
        $memoryIntensity = $this->getMemoryIntensity();
        $timeIntensity = $this->getTimeIntensity();
        $cpuIntensity = $this->getCpuIntensity();

        $maxIntensity = max($memoryIntensity, $timeIntensity, $cpuIntensity);

        return match ($maxIntensity) {
            1 => 'low',
            2 => 'medium',
            3 => 'high',
            4 => 'very_high',
            default => 'unknown'
        };
    }

    public function isPerformant(): bool
    {
        return $this->success &&
               $this->executionTime <= 2.0 &&          // Under 2 seconds
               $this->memoryUsage <= 50 * 1024 * 1024 && // Under 50MB
               $this->cpuUsage <= 1.0;                  // Under 1 second CPU
    }

    public function hasPerformanceIssues(): bool
    {
        return ! $this->success ||
               $this->executionTime > 10.0 ||           // Over 10 seconds
               $this->memoryUsage > 200 * 1024 * 1024 || // Over 200MB
               $this->cpuUsage > 5.0;                   // Over 5 seconds CPU
    }

    public function getPerformanceIssues(): array
    {
        $issues = [];

        if (! $this->success) {
            $issues[] = [
                'type' => 'execution_failure',
                'severity' => 'critical',
                'message' => 'Execution failed with error',
                'details' => $this->error,
            ];
        }

        if ($this->executionTime > 10.0) {
            $issues[] = [
                'type' => 'slow_execution',
                'severity' => 'high',
                'message' => 'Execution time exceeds 10 seconds',
                'details' => $this->formatExecutionTime(),
            ];
        } elseif ($this->executionTime > 5.0) {
            $issues[] = [
                'type' => 'slow_execution',
                'severity' => 'medium',
                'message' => 'Execution time exceeds 5 seconds',
                'details' => $this->formatExecutionTime(),
            ];
        }

        if ($this->memoryUsage > 200 * 1024 * 1024) {
            $issues[] = [
                'type' => 'high_memory_usage',
                'severity' => 'high',
                'message' => 'Memory usage exceeds 200MB',
                'details' => $this->formatBytes($this->memoryUsage),
            ];
        } elseif ($this->memoryUsage > 100 * 1024 * 1024) {
            $issues[] = [
                'type' => 'high_memory_usage',
                'severity' => 'medium',
                'message' => 'Memory usage exceeds 100MB',
                'details' => $this->formatBytes($this->memoryUsage),
            ];
        }

        if ($this->cpuUsage > 5.0) {
            $issues[] = [
                'type' => 'high_cpu_usage',
                'severity' => 'high',
                'message' => 'CPU usage exceeds 5 seconds',
                'details' => $this->formatCpuUsage(),
            ];
        } elseif ($this->cpuUsage > 2.0) {
            $issues[] = [
                'type' => 'high_cpu_usage',
                'severity' => 'medium',
                'message' => 'CPU usage exceeds 2 seconds',
                'details' => $this->formatCpuUsage(),
            ];
        }

        return $issues;
    }

    public function getOptimizationSuggestions(): array
    {
        $suggestions = [];

        if ($this->executionTime > 5.0) {
            $suggestions[] = [
                'type' => 'execution_time',
                'suggestion' => 'Consider implementing caching or optimizing algorithms',
                'impact' => 'high',
                'effort' => 'medium',
            ];
        }

        if ($this->memoryUsage > 100 * 1024 * 1024) {
            $suggestions[] = [
                'type' => 'memory_usage',
                'suggestion' => 'Implement data streaming or pagination for large datasets',
                'impact' => 'medium',
                'effort' => 'medium',
            ];
        }

        if ($this->resultSize > 10 * 1024 * 1024) {
            $suggestions[] = [
                'type' => 'result_size',
                'suggestion' => 'Enable result compression or implement data filtering',
                'impact' => 'medium',
                'effort' => 'low',
            ];
        }

        if ($this->cpuUsage > 3.0) {
            $suggestions[] = [
                'type' => 'cpu_usage',
                'suggestion' => 'Profile code to identify CPU bottlenecks',
                'impact' => 'high',
                'effort' => 'high',
            ];
        }

        return $suggestions;
    }

    public function compare(self $other): array
    {
        return [
            'execution_time' => [
                'current' => $this->executionTime,
                'previous' => $other->executionTime,
                'change' => $this->executionTime - $other->executionTime,
                'change_percentage' => $other->executionTime > 0
                    ? (($this->executionTime - $other->executionTime) / $other->executionTime) * 100
                    : 0,
                'improvement' => $this->executionTime < $other->executionTime,
            ],
            'memory_usage' => [
                'current' => $this->memoryUsage,
                'previous' => $other->memoryUsage,
                'change' => $this->memoryUsage - $other->memoryUsage,
                'change_percentage' => $other->memoryUsage > 0
                    ? (($this->memoryUsage - $other->memoryUsage) / $other->memoryUsage) * 100
                    : 0,
                'improvement' => $this->memoryUsage < $other->memoryUsage,
            ],
            'cpu_usage' => [
                'current' => $this->cpuUsage,
                'previous' => $other->cpuUsage,
                'change' => $this->cpuUsage - $other->cpuUsage,
                'change_percentage' => $other->cpuUsage > 0
                    ? (($this->cpuUsage - $other->cpuUsage) / $other->cpuUsage) * 100
                    : 0,
                'improvement' => $this->cpuUsage < $other->cpuUsage,
            ],
            'overall_improvement' => $this->calculateEfficiencyScore() > $other->calculateEfficiencyScore(),
        ];
    }

    private function calculateTimeScore(): float
    {
        // Score based on execution time (lower is better)
        return match (true) {
            $this->executionTime <= 0.5 => 100,   // Excellent
            $this->executionTime <= 1.0 => 90,    // Very good
            $this->executionTime <= 2.0 => 80,    // Good
            $this->executionTime <= 5.0 => 60,    // Acceptable
            $this->executionTime <= 10.0 => 30,   // Poor
            default => 10                          // Very poor
        };
    }

    private function calculateMemoryScore(): float
    {
        // Score based on memory usage (lower is better)
        $memoryMB = $this->memoryUsage / (1024 * 1024);

        return match (true) {
            $memoryMB <= 10 => 100,    // Excellent
            $memoryMB <= 25 => 90,     // Very good
            $memoryMB <= 50 => 80,     // Good
            $memoryMB <= 100 => 60,    // Acceptable
            $memoryMB <= 200 => 30,    // Poor
            default => 10              // Very poor
        };
    }

    private function calculateCpuScore(): float
    {
        // Score based on CPU usage (lower is better)
        return match (true) {
            $this->cpuUsage <= 0.1 => 100,    // Excellent
            $this->cpuUsage <= 0.5 => 90,     // Very good
            $this->cpuUsage <= 1.0 => 80,     // Good
            $this->cpuUsage <= 2.0 => 60,     // Acceptable
            $this->cpuUsage <= 5.0 => 30,     // Poor
            default => 10                      // Very poor
        };
    }

    private function calculateResultScore(): float
    {
        // Score based on result size efficiency (smaller relative to processing is better)
        if ($this->resultSize === 0) {
            return 50; // Neutral score for no result
        }

        $sizeMB = $this->resultSize / (1024 * 1024);

        return match (true) {
            $sizeMB <= 1 => 100,       // Excellent
            $sizeMB <= 5 => 90,        // Very good
            $sizeMB <= 10 => 80,       // Good
            $sizeMB <= 25 => 60,       // Acceptable
            $sizeMB <= 50 => 30,       // Poor
            default => 10              // Very poor
        };
    }

    private function getMemoryIntensity(): int
    {
        $memoryMB = $this->memoryUsage / (1024 * 1024);

        return match (true) {
            $memoryMB <= 25 => 1,      // Low
            $memoryMB <= 100 => 2,     // Medium
            $memoryMB <= 300 => 3,     // High
            default => 4               // Very high
        };
    }

    private function getTimeIntensity(): int
    {
        return match (true) {
            $this->executionTime <= 1.0 => 1,     // Low
            $this->executionTime <= 5.0 => 2,     // Medium
            $this->executionTime <= 15.0 => 3,    // High
            default => 4                           // Very high
        };
    }

    private function getCpuIntensity(): int
    {
        return match (true) {
            $this->cpuUsage <= 0.5 => 1,      // Low
            $this->cpuUsage <= 2.0 => 2,      // Medium
            $this->cpuUsage <= 6.0 => 3,      // High
            default => 4                       // Very high
        };
    }

    private function formatExecutionTime(): string
    {
        if ($this->executionTime < 0.001) {
            return round($this->executionTime * 1000000, 1) . ' μs';
        }

        if ($this->executionTime < 1.0) {
            return round($this->executionTime * 1000, 1) . ' ms';
        }

        return round($this->executionTime, 3) . ' s';
    }

    private function formatCpuUsage(): string
    {
        return round($this->cpuUsage, 3) . ' s';
    }

    private function formatBytes(int $bytes): string
    {
        if ($bytes === 0) {
            return '0 B';
        }

        $units = ['B', 'KB', 'MB', 'GB'];
        $unitIndex = 0;
        $value = $bytes;

        while ($value >= 1024 && $unitIndex < count($units) - 1) {
            $value /= 1024;
            $unitIndex++;
        }

        return round($value, 2) . ' ' . $units[$unitIndex];
    }
}
