<?php

declare(strict_types=1);

namespace App\Framework\Performance\ValueObjects;

use App\Framework\Core\ValueObjects\Timestamp;

/**
 * Value Object representing a complete performance report
 */
final readonly class PerformanceReport
{
    /**
     * @param Timestamp $timestamp When the report was generated
     * @param PerformanceSummary $summary Overall performance summary
     * @param array<string, CategoryMetrics> $categories Metrics grouped by category
     * @param array<string, array<string, mixed>> $metrics Individual metric details
     */
    public function __construct(
        public Timestamp $timestamp,
        public PerformanceSummary $summary,
        public array $categories,
        public array $metrics
    ) {
    }

    /**
     * Convert to array for backward compatibility and serialization
     * @return array{
     *     timestamp: string,
     *     summary: array<string, mixed>,
     *     categories: array<string, array<string, mixed>>,
     *     metrics: array<string, array<string, mixed>>
     * }
     */
    public function toArray(): array
    {
        $categoriesArray = [];
        foreach ($this->categories as $name => $category) {
            $categoriesArray[$name] = $category->toArray();
        }

        return [
            'timestamp' => $this->timestamp->format('Y-m-d H:i:s'),
            'summary' => $this->summary->toArray(),
            'categories' => $categoriesArray,
            'metrics' => $this->metrics,
        ];
    }

    /**
     * Get total request time in milliseconds
     */
    public function getTotalRequestTime(): float
    {
        return $this->summary->totalRequestTime->toMilliseconds();
    }

    /**
     * Get total memory usage in bytes
     */
    public function getTotalMemoryUsage(): int
    {
        return $this->summary->totalRequestMemory->toBytes();
    }

    /**
     * Get peak memory usage in bytes
     */
    public function getPeakMemoryUsage(): int
    {
        return $this->summary->peakMemory->toBytes();
    }

    /**
     * Get metrics for a specific category
     */
    public function getCategoryMetrics(string $category): ?CategoryMetrics
    {
        return $this->categories[$category] ?? null;
    }

    /**
     * Get all category names
     * @return array<string>
     */
    public function getCategoryNames(): array
    {
        return array_keys($this->categories);
    }

    /**
     * Get total number of metrics
     */
    public function getMetricsCount(): int
    {
        return $this->summary->metricsCount;
    }

    /**
     * Check if report has any metrics
     */
    public function hasMetrics(): bool
    {
        return $this->summary->metricsCount > 0;
    }

    /**
     * Get memory summary
     */
    public function getMemorySummary(): MemorySummary
    {
        return $this->summary->memorySummary;
    }

    /**
     * Get top metrics by time
     * @return array<array<string, mixed>>
     */
    public function getTopMetricsByTime(int $limit = 10): array
    {
        $metricsWithTime = array_filter($this->metrics, function ($metric) {
            return $metric['measurements']['total_duration_ms'] > 0;
        });

        usort($metricsWithTime, function ($a, $b) {
            return $b['measurements']['total_duration_ms'] <=> $a['measurements']['total_duration_ms'];
        });

        return array_slice($metricsWithTime, 0, $limit);
    }

    /**
     * Get top metrics by memory
     * @return array<array<string, mixed>>
     */
    public function getTopMetricsByMemory(int $limit = 10): array
    {
        $metricsWithMemory = array_filter($this->metrics, function ($metric) {
            return $metric['measurements']['total_memory_bytes'] > 0;
        });

        usort($metricsWithMemory, function ($a, $b) {
            return $b['measurements']['total_memory_bytes'] <=> $a['measurements']['total_memory_bytes'];
        });

        return array_slice($metricsWithMemory, 0, $limit);
    }
}
