<?php

declare(strict_types=1);

namespace Tests\Performance\Benchmarks;

use App\Framework\Performance\Contracts\PerformanceCollectorInterface;
use App\Framework\Performance\PerformanceCategory;
use App\Framework\Cache\Cache;
use App\Framework\Cache\CacheKey;
use App\Framework\Cache\CacheItem;
use App\Framework\Core\ValueObjects\Duration;
use Tests\Performance\PerformanceTestCase;
use Tests\Performance\PerformanceBenchmarkResult;

/**
 * Performance benchmarks for caching system
 *
 * Tests cache performance including:
 * - Get/Set operations
 * - Batch operations
 * - Cache miss handling
 * - Different data sizes
 */
final readonly class CacheBenchmark extends PerformanceTestCase
{
    public function __construct(
        PerformanceCollectorInterface $collector,
        private Cache $cache
    ) {
        parent::__construct($collector);
    }

    /**
     * Benchmark simple cache SET operation
     */
    public function benchmarkCacheSet(): PerformanceBenchmarkResult
    {
        $key = CacheKey::fromString('benchmark_test');
        $ttl = Duration::fromMinutes(5);

        $result = $this->benchmark(
            operation: fn() => $this->cache->set(
                CacheItem::forSetting($key, 'test_value', $ttl)
            ),
            iterations: 10000,
            name: 'Cache SET'
        );

        // Cache SET should be very fast
        $this->assertPerformanceThreshold($result, maxAvgTimeMs: 0.5);

        $this->recordBenchmark($result, PerformanceCategory::CACHE);

        return $result;
    }

    /**
     * Benchmark simple cache GET operation
     */
    public function benchmarkCacheGet(): PerformanceBenchmarkResult
    {
        // Warmup: Set a value first
        $key = CacheKey::fromString('benchmark_get_test');
        $this->cache->set(
            CacheItem::forSetting($key, 'test_value', Duration::fromHours(1))
        );

        $result = $this->benchmark(
            operation: fn() => $this->cache->get($key),
            iterations: 10000,
            name: 'Cache GET (hit)'
        );

        // Cache GET should be extremely fast
        $this->assertPerformanceThreshold($result, maxAvgTimeMs: 0.2);

        $this->recordBenchmark($result, PerformanceCategory::CACHE);

        return $result;
    }

    /**
     * Benchmark cache GET operation (miss)
     */
    public function benchmarkCacheGetMiss(): PerformanceBenchmarkResult
    {
        $key = CacheKey::fromString('non_existent_key');

        $result = $this->benchmark(
            operation: fn() => $this->cache->get($key),
            iterations: 10000,
            name: 'Cache GET (miss)'
        );

        // Cache miss should still be fast
        $this->assertPerformanceThreshold($result, maxAvgTimeMs: 0.3);

        $this->recordBenchmark($result, PerformanceCategory::CACHE);

        return $result;
    }

    /**
     * Benchmark cache remember pattern
     */
    public function benchmarkCacheRemember(): PerformanceBenchmarkResult
    {
        $key = CacheKey::fromString('benchmark_remember');
        $ttl = Duration::fromMinutes(10);

        $result = $this->benchmark(
            operation: fn() => $this->cache->remember(
                key: $key,
                callback: fn() => ['computed' => 'value'],
                ttl: $ttl
            ),
            iterations: 5000,
            name: 'Cache Remember Pattern'
        );

        $this->assertPerformanceThreshold($result, maxAvgTimeMs: 1.0);

        $this->recordBenchmark($result, PerformanceCategory::CACHE);

        return $result;
    }

    /**
     * Benchmark batch cache SET operations
     */
    public function benchmarkBatchSet(): PerformanceBenchmarkResult
    {
        $items = [];
        for ($i = 0; $i < 100; $i++) {
            $items[] = CacheItem::forSetting(
                key: CacheKey::fromString("batch_key_{$i}"),
                value: "value_{$i}",
                ttl: Duration::fromMinutes(5)
            );
        }

        $result = $this->benchmark(
            operation: fn() => $this->cache->set(...$items),
            iterations: 100,
            name: 'Batch SET (100 items)'
        );

        // Batch operations should be efficient
        $this->assertPerformanceThreshold($result, maxAvgTimeMs: 20.0);

        $this->recordBenchmark($result, PerformanceCategory::CACHE);

        return $result;
    }

    /**
     * Benchmark cache SET with large data
     */
    public function benchmarkSetLargeData(): PerformanceBenchmarkResult
    {
        // Generate 1MB of data
        $largeData = str_repeat('A', 1024 * 1024);
        $key = CacheKey::fromString('large_data_key');

        $result = $this->benchmark(
            operation: fn() => $this->cache->set(
                CacheItem::forSetting($key, $largeData, Duration::fromMinutes(5))
            ),
            iterations: 100,
            name: 'Cache SET (1MB data)'
        );

        // Large data should still be reasonably fast
        $this->assertPerformanceThreshold(
            $result,
            maxAvgTimeMs: 10.0,
            maxMemoryBytes: 5 * 1024 * 1024 // 5MB
        );

        $this->recordBenchmark($result, PerformanceCategory::CACHE);

        return $result;
    }

    /**
     * Benchmark cache DELETE operation
     */
    public function benchmarkCacheDelete(): PerformanceBenchmarkResult
    {
        // Warmup: Set values first
        for ($i = 0; $i < 10; $i++) {
            $key = CacheKey::fromString("delete_test_{$i}");
            $this->cache->set(
                CacheItem::forSetting($key, "value_{$i}", Duration::fromMinutes(5))
            );
        }

        $counter = 0;
        $result = $this->benchmark(
            operation: function() use (&$counter) {
                $key = CacheKey::fromString("delete_test_" . ($counter++ % 10));
                $this->cache->forget($key);
            },
            iterations: 10000,
            name: 'Cache DELETE'
        );

        $this->assertPerformanceThreshold($result, maxAvgTimeMs: 0.5);

        $this->recordBenchmark($result, PerformanceCategory::CACHE);

        return $result;
    }

    /**
     * Benchmark cache HAS operation
     */
    public function benchmarkCacheHas(): PerformanceBenchmarkResult
    {
        // Warmup: Set a value
        $key = CacheKey::fromString('has_test');
        $this->cache->set(
            CacheItem::forSetting($key, 'value', Duration::fromMinutes(5))
        );

        $result = $this->benchmark(
            operation: fn() => $this->cache->has($key),
            iterations: 10000,
            name: 'Cache HAS'
        );

        $this->assertPerformanceThreshold($result, maxAvgTimeMs: 0.2);

        $this->recordBenchmark($result, PerformanceCategory::CACHE);

        return $result;
    }

    /**
     * Run all cache benchmarks and return summary
     */
    public function runAllBenchmarks(): array
    {
        return [
            'cache_set' => $this->benchmarkCacheSet(),
            'cache_get' => $this->benchmarkCacheGet(),
            'cache_get_miss' => $this->benchmarkCacheGetMiss(),
            'cache_remember' => $this->benchmarkCacheRemember(),
            'batch_set' => $this->benchmarkBatchSet(),
            'set_large_data' => $this->benchmarkSetLargeData(),
            'cache_delete' => $this->benchmarkCacheDelete(),
            'cache_has' => $this->benchmarkCacheHas(),
        ];
    }
}
