<?php

declare(strict_types=1);

use App\Framework\Core\ValueObjects\Percentage;
use App\Framework\LiveComponents\Cache\CacheMetrics;
use App\Framework\LiveComponents\Cache\CacheType;

describe('CacheMetrics', function () {
    it('creates empty metrics with zero values', function () {
        $metrics = CacheMetrics::empty(CacheType::STATE);

        expect($metrics->cacheType)->toBe(CacheType::STATE)
            ->and($metrics->hits)->toBe(0)
            ->and($metrics->misses)->toBe(0)
            ->and($metrics->hitRate->getValue())->toBe(0.0)
            ->and($metrics->missRate->getValue())->toBe(0.0)
            ->and($metrics->getTotalOperations())->toBe(0);
    });

    it('records cache hit and updates metrics', function () {
        $metrics = CacheMetrics::empty(CacheType::STATE);
        $updated = $metrics->withHit(0.5);

        expect($updated->hits)->toBe(1)
            ->and($updated->misses)->toBe(0)
            ->and($updated->hitRate->getValue())->toBe(100.0)
            ->and($updated->missRate->getValue())->toBe(0.0)
            ->and($updated->averageLookupTimeMs)->toBe(0.5);
    });

    it('records cache miss and updates metrics', function () {
        $metrics = CacheMetrics::empty(CacheType::STATE);
        $updated = $metrics->withMiss(1.2);

        expect($updated->hits)->toBe(0)
            ->and($updated->misses)->toBe(1)
            ->and($updated->hitRate->getValue())->toBe(0.0)
            ->and($updated->missRate->getValue())->toBe(100.0)
            ->and($updated->averageLookupTimeMs)->toBe(1.2);
    });

    it('calculates correct hit rate with mixed hits and misses', function () {
        $metrics = CacheMetrics::empty(CacheType::STATE)
            ->withHit(0.5)
            ->withHit(0.6)
            ->withHit(0.4)
            ->withMiss(1.0);

        expect($metrics->hits)->toBe(3)
            ->and($metrics->misses)->toBe(1)
            ->and($metrics->hitRate->getValue())->toBe(75.0)
            ->and($metrics->missRate->getValue())->toBe(25.0)
            ->and($metrics->getTotalOperations())->toBe(4);
    });

    it('calculates weighted average lookup time', function () {
        $metrics = CacheMetrics::empty(CacheType::STATE)
            ->withHit(0.5)   // Avg: 0.5
            ->withHit(1.5);  // Avg: (0.5 + 1.5) / 2 = 1.0

        expect($metrics->averageLookupTimeMs)->toBe(1.0);
    });

    it('records cache invalidations', function () {
        $metrics = CacheMetrics::empty(CacheType::STATE)
            ->withInvalidation()
            ->withInvalidation();

        expect($metrics->invalidations)->toBe(2);
    });

    it('updates cache size', function () {
        $metrics = CacheMetrics::empty(CacheType::STATE)
            ->withSize(150);

        expect($metrics->totalSize)->toBe(150);
    });

    it('checks performance target with Percentage', function () {
        $metrics = CacheMetrics::empty(CacheType::STATE)
            ->withHit(0.5)
            ->withHit(0.6)
            ->withHit(0.7)
            ->withMiss(1.0); // 75% hit rate

        $target = Percentage::from(70.0);

        expect($metrics->meetsPerformanceTarget($target))->toBeTrue();
    });

    it('returns correct performance grade', function () {
        expect(
            CacheMetrics::empty(CacheType::STATE)
            ->withHit(0.5)->withHit(0.5)->withHit(0.5)->withHit(0.5)
            ->withHit(0.5)->withHit(0.5)->withHit(0.5)->withHit(0.5)
            ->withHit(0.5)->withMiss(1.0) // 90% hit rate
            ->getPerformanceGrade()
        )->toBe('A');

        expect(
            CacheMetrics::empty(CacheType::STATE)
            ->withHit(0.5)->withHit(0.5)->withHit(0.5)->withHit(0.5)
            ->withMiss(1.0)->withMiss(1.0) // 66.67% hit rate
            ->getPerformanceGrade()
        )->toBe('D');
    });

    it('converts to array for reporting', function () {
        $metrics = CacheMetrics::empty(CacheType::STATE)
            ->withHit(0.5)
            ->withMiss(1.0);

        $array = $metrics->toArray();

        expect($array)->toHaveKey('cache_type')
            ->and($array)->toHaveKey('hits')
            ->and($array)->toHaveKey('misses')
            ->and($array)->toHaveKey('hit_rate')
            ->and($array)->toHaveKey('miss_rate')
            ->and($array)->toHaveKey('average_lookup_time_ms')
            ->and($array)->toHaveKey('performance_grade')
            ->and($array['cache_type'])->toBe('state')
            ->and($array['hits'])->toBe(1)
            ->and($array['misses'])->toBe(1);
    });

    it('merges metrics from multiple sources', function () {
        $metrics1 = CacheMetrics::empty(CacheType::STATE)
            ->withHit(0.5)
            ->withHit(0.6);

        $metrics2 = CacheMetrics::empty(CacheType::SLOT)
            ->withHit(0.7)
            ->withMiss(1.0);

        $merged = CacheMetrics::merge($metrics1, $metrics2);

        expect($merged->cacheType)->toBe(CacheType::MERGED)
            ->and($merged->hits)->toBe(3)
            ->and($merged->misses)->toBe(1)
            ->and($merged->hitRate->getValue())->toBe(75.0);
    });

    it('handles empty merge gracefully', function () {
        $merged = CacheMetrics::merge();

        expect($merged->cacheType)->toBe(CacheType::MERGED)
            ->and($merged->hits)->toBe(0)
            ->and($merged->misses)->toBe(0);
    });

    it('preserves immutability when recording operations', function () {
        $original = CacheMetrics::empty(CacheType::STATE);
        $updated = $original->withHit(0.5);

        expect($original->hits)->toBe(0)
            ->and($updated->hits)->toBe(1)
            ->and($original)->not->toBe($updated);
    });
});
