<?php

declare(strict_types=1);

namespace App\Framework\Performance\ValueObjects;

use App\Framework\Core\ValueObjects\Byte;
use App\Framework\Core\ValueObjects\Duration;

/**
 * Value Object representing performance summary statistics
 */
final readonly class PerformanceSummary
{
    public function __construct(
        public Duration $totalRequestTime,
        public Byte $totalRequestMemory,
        public Byte $peakMemory,
        public int $metricsCount,
        public MemorySummary $memorySummary
    ) {
    }

    /**
     * Create from raw values (for backward compatibility)
     */
    public static function fromRawValues(
        float $totalRequestTimeMs,
        int $totalRequestMemoryBytes,
        int $peakMemoryBytes,
        int $metricsCount,
        MemorySummary $memorySummary
    ): self {
        return new self(
            totalRequestTime: Duration::fromSeconds($totalRequestTimeMs / 1000.0),
            totalRequestMemory: Byte::fromBytes($totalRequestMemoryBytes),
            peakMemory: Byte::fromBytes($peakMemoryBytes),
            metricsCount: $metricsCount,
            memorySummary: $memorySummary
        );
    }

    /**
     * Convert to array for backward compatibility
     * @return array{
     *     total_request_time_ms: float,
     *     total_request_memory_bytes: int,
     *     peak_memory_bytes: int,
     *     metrics_count: int,
     *     memory_summary: MemorySummary
     * }
     */
    public function toArray(): array
    {
        return [
            'total_request_time_ms' => $this->totalRequestTime->toMilliseconds(),
            'total_request_memory_bytes' => $this->totalRequestMemory->toBytes(),
            'peak_memory_bytes' => $this->peakMemory->toBytes(),
            'metrics_count' => $this->metricsCount,
            'memory_summary' => $this->memorySummary,
        ];
    }

    /**
     * Get formatted request time
     */
    public function getFormattedRequestTime(): string
    {
        return sprintf('%.2f ms', $this->totalRequestTime->toMilliseconds());
    }

    /**
     * Get formatted memory usage
     */
    public function getFormattedMemoryUsage(): string
    {
        return $this->totalRequestMemory->toHumanReadable();
    }

    /**
     * Get formatted peak memory
     */
    public function getFormattedPeakMemory(): string
    {
        return $this->peakMemory->toHumanReadable();
    }

    /**
     * Check if performance is within acceptable bounds
     */
    public function isPerformanceAcceptable(Duration $maxTime, Byte $maxMemory): bool
    {
        return $this->totalRequestTime->toMilliseconds() <= $maxTime->toMilliseconds()
            && $this->peakMemory->toBytes() <= $maxMemory->toBytes();
    }

    /**
     * Get performance grade (A-F)
     */
    public function getPerformanceGrade(): string
    {
        $timeMs = $this->totalRequestTime->toMilliseconds();
        $memoryMb = $this->peakMemory->toMegabytes();

        // Time-based grading (50% weight)
        $timeGrade = match (true) {
            $timeMs < 100 => 5,   // A
            $timeMs < 300 => 4,   // B
            $timeMs < 500 => 3,   // C
            $timeMs < 1000 => 2,  // D
            default => 1,          // F
        };

        // Memory-based grading (50% weight)
        $memoryGrade = match (true) {
            $memoryMb < 32 => 5,   // A
            $memoryMb < 64 => 4,   // B
            $memoryMb < 128 => 3,  // C
            $memoryMb < 256 => 2,  // D
            default => 1,          // F
        };

        $averageGrade = ($timeGrade + $memoryGrade) / 2;

        return match (true) {
            $averageGrade >= 4.5 => 'A',
            $averageGrade >= 3.5 => 'B',
            $averageGrade >= 2.5 => 'C',
            $averageGrade >= 1.5 => 'D',
            default => 'F',
        };
    }

    /**
     * Get performance as percentage (0-100)
     */
    public function getPerformanceScore(): int
    {
        $timeMs = $this->totalRequestTime->toMilliseconds();
        $memoryMb = $this->peakMemory->toMegabytes();

        // Time score (50% weight) - inverse scale
        $timeScore = match (true) {
            $timeMs < 100 => 100,
            $timeMs < 300 => 80,
            $timeMs < 500 => 60,
            $timeMs < 1000 => 40,
            $timeMs < 2000 => 20,
            default => 0,
        };

        // Memory score (50% weight) - inverse scale
        $memoryScore = match (true) {
            $memoryMb < 32 => 100,
            $memoryMb < 64 => 80,
            $memoryMb < 128 => 60,
            $memoryMb < 256 => 40,
            $memoryMb < 512 => 20,
            default => 0,
        };

        return (int) round(($timeScore + $memoryScore) / 2);
    }
}
