<?php

declare(strict_types=1);

namespace App\Framework\LiveComponents\Cache;

use App\Framework\Core\ValueObjects\Percentage;

/**
 * Cache Metrics Collector
 *
 * Collects and aggregates cache performance metrics across all cache layers.
 *
 * Features:
 * - Real-time metric collection
 * - Per-cache-type metrics tracking
 * - Aggregate metrics across all caches
 * - Performance target validation
 * - Metric reporting and export
 */
final class CacheMetricsCollector
{
    /** @var array<string, CacheMetrics> */
    private array $metrics = [];

    public function __construct()
    {
        // Initialize metrics for each cache type
        $this->metrics[CacheType::STATE->value] = CacheMetrics::empty(CacheType::STATE);
        $this->metrics[CacheType::SLOT->value] = CacheMetrics::empty(CacheType::SLOT);
        $this->metrics[CacheType::TEMPLATE->value] = CacheMetrics::empty(CacheType::TEMPLATE);
    }

    /**
     * Record cache hit
     */
    public function recordHit(CacheType $cacheType, float $lookupTimeMs): void
    {
        $key = $cacheType->value;
        $this->metrics[$key] = $this->metrics[$key]->withHit($lookupTimeMs);
    }

    /**
     * Record cache miss
     */
    public function recordMiss(CacheType $cacheType, float $lookupTimeMs): void
    {
        $key = $cacheType->value;
        $this->metrics[$key] = $this->metrics[$key]->withMiss($lookupTimeMs);
    }

    /**
     * Record cache invalidation
     */
    public function recordInvalidation(CacheType $cacheType): void
    {
        $key = $cacheType->value;
        $this->metrics[$key] = $this->metrics[$key]->withInvalidation();
    }

    /**
     * Update cache size
     */
    public function updateSize(CacheType $cacheType, int $size): void
    {
        $key = $cacheType->value;
        $this->metrics[$key] = $this->metrics[$key]->withSize($size);
    }

    /**
     * Get metrics for specific cache type
     */
    public function getMetrics(CacheType $cacheType): CacheMetrics
    {
        return $this->metrics[$cacheType->value];
    }

    /**
     * Get metrics for all cache types
     *
     * @return array<string, CacheMetrics>
     */
    public function getAllMetrics(): array
    {
        return $this->metrics;
    }

    /**
     * Get aggregate metrics across all caches
     */
    public function getAggregateMetrics(): CacheMetrics
    {
        return CacheMetrics::merge(...array_values($this->metrics));
    }

    /**
     * Get metrics summary for reporting
     */
    public function getSummary(): array
    {
        $aggregate = $this->getAggregateMetrics();

        return [
            'overall' => $aggregate->toArray(),
            'by_type' => [
                'state' => $this->metrics[CacheType::STATE->value]->toArray(),
                'slot' => $this->metrics[CacheType::SLOT->value]->toArray(),
                'template' => $this->metrics[CacheType::TEMPLATE->value]->toArray(),
            ],
            'performance_assessment' => $this->assessPerformance(),
        ];
    }

    /**
     * Assess overall cache performance
     */
    public function assessPerformance(): array
    {
        $stateMetrics = $this->metrics[CacheType::STATE->value];
        $slotMetrics = $this->metrics[CacheType::SLOT->value];
        $templateMetrics = $this->metrics[CacheType::TEMPLATE->value];

        // Performance targets
        $stateTarget = Percentage::from(70.0);   // ~70% faster initialization
        $slotTarget = Percentage::from(60.0);    // ~60% faster resolution
        $templateTarget = Percentage::from(80.0); // ~80% faster rendering

        return [
            'state_cache' => [
                'target' => $stateTarget->format(1),
                'actual' => $stateMetrics->hitRate->format(2),
                'meets_target' => $stateMetrics->meetsPerformanceTarget($stateTarget),
                'grade' => $stateMetrics->getPerformanceGrade(),
            ],
            'slot_cache' => [
                'target' => $slotTarget->format(1),
                'actual' => $slotMetrics->hitRate->format(2),
                'meets_target' => $slotMetrics->meetsPerformanceTarget($slotTarget),
                'grade' => $slotMetrics->getPerformanceGrade(),
            ],
            'template_cache' => [
                'target' => $templateTarget->format(1),
                'actual' => $templateMetrics->hitRate->format(2),
                'meets_target' => $templateMetrics->meetsPerformanceTarget($templateTarget),
                'grade' => $templateMetrics->getPerformanceGrade(),
            ],
            'overall_grade' => $this->getAggregateMetrics()->getPerformanceGrade(),
        ];
    }

    /**
     * Reset all metrics
     */
    public function reset(): void
    {
        $this->metrics[CacheType::STATE->value] = CacheMetrics::empty(CacheType::STATE);
        $this->metrics[CacheType::SLOT->value] = CacheMetrics::empty(CacheType::SLOT);
        $this->metrics[CacheType::TEMPLATE->value] = CacheMetrics::empty(CacheType::TEMPLATE);
    }

    /**
     * Export metrics for external monitoring
     */
    public function export(): array
    {
        return [
            'timestamp' => time(),
            'metrics' => $this->getSummary(),
        ];
    }

    /**
     * Check if any cache is underperforming
     */
    public function hasPerformanceIssues(): bool
    {
        $assessment = $this->assessPerformance();

        return ! $assessment['state_cache']['meets_target']
            || ! $assessment['slot_cache']['meets_target']
            || ! $assessment['template_cache']['meets_target'];
    }

    /**
     * Get performance warnings
     *
     * @return array<string>
     */
    public function getPerformanceWarnings(): array
    {
        $warnings = [];
        $assessment = $this->assessPerformance();

        if (! $assessment['state_cache']['meets_target']) {
            $warnings[] = sprintf(
                'State cache hit rate (%s) below target (%s)',
                $assessment['state_cache']['actual'],
                $assessment['state_cache']['target']
            );
        }

        if (! $assessment['slot_cache']['meets_target']) {
            $warnings[] = sprintf(
                'Slot cache hit rate (%s) below target (%s)',
                $assessment['slot_cache']['actual'],
                $assessment['slot_cache']['target']
            );
        }

        if (! $assessment['template_cache']['meets_target']) {
            $warnings[] = sprintf(
                'Template cache hit rate (%s) below target (%s)',
                $assessment['template_cache']['actual'],
                $assessment['template_cache']['target']
            );
        }

        return $warnings;
    }
}
