<?php

declare(strict_types=1);

use App\Framework\Cache\Cache;
use App\Framework\Cache\CacheItem;
use App\Framework\Cache\CacheKey;
use App\Framework\Cache\CacheResult;
use App\Framework\View\Caching\Analysis\CacheStrategy;
use App\Framework\View\Caching\Analysis\SmartTemplateAnalyzer;
use App\Framework\View\Caching\CacheManager;
use App\Framework\View\Caching\FragmentCache;
use App\Framework\View\Caching\TemplateContext;
use App\Framework\View\Loading\TemplateLoader;
use App\Framework\Core\PathProvider;

describe('Caching Performance Benchmarks', function () {
    beforeEach(function () {
        // Create test templates directory
        $this->testDir = '/home/michael/dev/michaelschiemer/tests/tmp/caching_perf_' . uniqid();
        if (!is_dir($this->testDir)) {
            mkdir($this->testDir, 0755, true);
        }

        // Create PathProvider
        $this->pathProvider = new PathProvider($this->testDir);

        // Create Cache mock
        $this->cache = Mockery::mock(Cache::class);

        // Create TemplateLoader
        $this->loader = new TemplateLoader(
            pathProvider: $this->pathProvider,
            cache: $this->cache,
            templates: [],
            templatePath: '',
            cacheEnabled: true
        );

        // Create Analyzer
        $this->analyzer = new SmartTemplateAnalyzer($this->loader);

        // Create FragmentCache mock
        $this->fragmentCache = Mockery::mock(FragmentCache::class);

        // Create CacheManager
        $this->cacheManager = new CacheManager(
            $this->cache,
            $this->analyzer,
            $this->fragmentCache
        );

        // Store results for reporting
        $this->perfResults = [];
    });

    afterEach(function () {
        // Cleanup test directory
        if (is_dir($this->testDir)) {
            $files = new RecursiveIteratorIterator(
                new RecursiveDirectoryIterator($this->testDir, RecursiveDirectoryIterator::SKIP_DOTS),
                RecursiveIteratorIterator::CHILD_FIRST
            );
            foreach ($files as $file) {
                $file->isDir() ? rmdir($file->getRealPath()) : unlink($file->getRealPath());
            }
            rmdir($this->testDir);
        }

        Mockery::close();

        // Print performance summary
        if (!empty($this->perfResults)) {
            echo "\n\n=== Performance Summary ===\n";
            foreach ($this->perfResults as $test => $results) {
                echo "\n{$test}:\n";

                // Check if this is timing stats or other metrics
                if (isset($results['min'])) {
                    echo "  Min:    " . number_format($results['min'], 2) . " ms\n";
                    echo "  Max:    " . number_format($results['max'], 2) . " ms\n";
                    echo "  Avg:    " . number_format($results['avg'], 2) . " ms\n";
                    echo "  Median: " . number_format($results['median'], 2) . " ms\n";
                    echo "  P95:    " . number_format($results['p95'], 2) . " ms\n";
                    echo "  P99:    " . number_format($results['p99'], 2) . " ms\n";
                }

                if (isset($results['memory'])) {
                    echo "  Memory: " . number_format($results['memory'], 2) . " MB\n";
                }

                if (isset($results['throughput_per_sec'])) {
                    echo "  Throughput: " . number_format($results['throughput_per_sec'], 0) . " renders/sec\n";
                }
            }
            echo "\n";
        }
    });

    describe('Cache Hit vs Miss Performance', function () {
        it('benchmarks cache hit performance (FULL_PAGE strategy)', function () {
            // Create large static template
            $content = '<html><head><title>Static Page</title></head><body>' . str_repeat('<p>Static content paragraph.</p>', 100) . '</body></html>';
            $filePath = $this->testDir . '/pages/static.view.php';
            $dir = dirname($filePath);
            if (!is_dir($dir)) mkdir($dir, 0755, true);
            file_put_contents($filePath, $content);

            $context = new TemplateContext(
                template: 'pages/static',
                data: ['title' => 'Test']
            );

            $renderedContent = $content;
            $measurements = [];

            // Warm up
            $this->cache->shouldReceive('get')->andReturn(CacheResult::fromItems(CacheItem::miss(CacheKey::fromString('test'))));
            $this->cache->shouldReceive('set')->andReturn(true);
            $this->cacheManager->render($context, fn() => $renderedContent);

            // Measure cache miss (10 iterations)
            $missMeasurements = [];
            for ($i = 0; $i < 10; $i++) {
                $this->cache->shouldReceive('get')->andReturn(CacheResult::fromItems(CacheItem::miss(CacheKey::fromString("test_{$i}"))));
                $this->cache->shouldReceive('set')->andReturn(true);

                $start = microtime(true);
                $this->cacheManager->render($context, fn() => $renderedContent);
                $missMeasurements[] = (microtime(true) - $start) * 1000;
            }

            // Measure cache hit (100 iterations)
            $hitMeasurements = [];
            $cacheKey = CacheKey::fromString('page:pages/static:' . md5(serialize(['title' => 'Test'])));
            $cacheItem = CacheItem::hit($cacheKey, $renderedContent);
            $this->cache->shouldReceive('get')->andReturn(CacheResult::fromItems($cacheItem));

            for ($i = 0; $i < 100; $i++) {
                $start = microtime(true);
                $this->cacheManager->render($context, fn() => $renderedContent);
                $hitMeasurements[] = (microtime(true) - $start) * 1000;
            }

            // Calculate statistics
            $missStats = calculateStats($missMeasurements);
            $hitStats = calculateStats($hitMeasurements);

            $this->perfResults['FULL_PAGE Cache Miss'] = $missStats;
            $this->perfResults['FULL_PAGE Cache Hit'] = $hitStats;

            // Assert performance characteristics (info-gathering, not strict benchmarks with mocks)
            // Note: Mocked cache doesn't show real performance gains, but tests the workflow
            expect($hitStats['avg'])->toBeLessThan(10.0); // Should complete in reasonable time
            expect($missStats['avg'])->toBeLessThan(10.0); // Should complete in reasonable time
        });

        it('benchmarks cache hit performance (COMPONENT strategy)', function () {
            // Create component template
            $content = '<button class="btn btn-primary">' . str_repeat('Click me ', 10) . '</button>';
            $filePath = $this->testDir . '/components/button.view.php';
            $dir = dirname($filePath);
            if (!is_dir($dir)) mkdir($dir, 0755, true);
            file_put_contents($filePath, $content);

            $context = new TemplateContext(
                template: 'components/button',
                data: ['text' => 'Submit']
            );

            $renderedContent = $content;

            // Measure cache miss (10 iterations)
            $missMeasurements = [];
            for ($i = 0; $i < 10; $i++) {
                $this->cache->shouldReceive('get')->andReturn(CacheResult::fromItems(CacheItem::miss(CacheKey::fromString("test_{$i}"))));
                $this->cache->shouldReceive('set')->andReturn(true);

                $start = microtime(true);
                $this->cacheManager->render($context, fn() => $renderedContent);
                $missMeasurements[] = (microtime(true) - $start) * 1000;
            }

            // Measure cache hit (100 iterations)
            $hitMeasurements = [];
            $cacheKey = CacheKey::fromString('component:button:' . md5(serialize(['text' => 'Submit'])));
            $cacheItem = CacheItem::hit($cacheKey, $renderedContent);
            $this->cache->shouldReceive('get')->andReturn(CacheResult::fromItems($cacheItem));

            for ($i = 0; $i < 100; $i++) {
                $start = microtime(true);
                $this->cacheManager->render($context, fn() => $renderedContent);
                $hitMeasurements[] = (microtime(true) - $start) * 1000;
            }

            $missStats = calculateStats($missMeasurements);
            $hitStats = calculateStats($hitMeasurements);

            $this->perfResults['COMPONENT Cache Miss'] = $missStats;
            $this->perfResults['COMPONENT Cache Hit'] = $hitStats;

            // Info-gathering: verify workflow completes in reasonable time
            expect($hitStats['avg'])->toBeLessThan(10.0);
            expect($missStats['avg'])->toBeLessThan(10.0);
        });
    });

    describe('SmartTemplateAnalyzer Performance', function () {
        it('benchmarks template analysis performance', function () {
            // Create various templates
            $templates = [
                'pages/simple.view.php' => '<html><body>Simple static page</body></html>',
                'pages/dynamic.view.php' => '<html><body>Hello {{ user.name }}, time: {{ now }}</body></html>',
                'components/card.view.php' => '<div class="card"><h3>{{ title }}</h3><p>{{ content }}</p></div>',
                'partials/nav.view.php' => '<nav><ul>' . str_repeat('<li>{{ item }}</li>', 5) . '</ul></nav>',
            ];

            foreach ($templates as $path => $content) {
                $filePath = $this->testDir . '/' . $path;
                $dir = dirname($filePath);
                if (!is_dir($dir)) mkdir($dir, 0755, true);
                file_put_contents($filePath, $content);
            }

            // Measure analysis performance
            $measurements = [];
            foreach ($templates as $path => $content) {
                $templateName = str_replace('.view.php', '', $path);

                $start = microtime(true);
                $analysis = $this->analyzer->analyze($templateName);
                $measurements[] = (microtime(true) - $start) * 1000;
            }

            // Repeat for statistical significance (25 iterations per template)
            for ($i = 0; $i < 24; $i++) {
                foreach ($templates as $path => $content) {
                    $templateName = str_replace('.view.php', '', $path);

                    $start = microtime(true);
                    $this->analyzer->analyze($templateName);
                    $measurements[] = (microtime(true) - $start) * 1000;
                }
            }

            $stats = calculateStats($measurements);
            $this->perfResults['Template Analysis'] = $stats;

            // Analysis should be fast (< 5ms average)
            expect($stats['avg'])->toBeLessThan(5.0);
            expect($stats['p99'])->toBeLessThan(10.0);
        });
    });

    describe('Memory Usage Benchmarks', function () {
        it('benchmarks memory usage across caching strategies', function () {
            $memoryBefore = memory_get_usage(true);

            // Create large templates
            $largeContent = str_repeat('<div class="item">Static content item</div>', 1000);

            // FULL_PAGE template
            $filePath = $this->testDir . '/pages/large.view.php';
            $dir = dirname($filePath);
            if (!is_dir($dir)) mkdir($dir, 0755, true);
            file_put_contents($filePath, $largeContent);

            $context = new TemplateContext(template: 'pages/large', data: []);

            // Mock cache responses
            $this->cache->shouldReceive('get')->andReturn(CacheResult::fromItems(CacheItem::miss(CacheKey::fromString('test'))));
            $this->cache->shouldReceive('set')->andReturn(true);

            // Render 100 times
            for ($i = 0; $i < 100; $i++) {
                $this->cacheManager->render($context, fn() => $largeContent);
            }

            $memoryAfter = memory_get_usage(true);
            $memoryUsed = ($memoryAfter - $memoryBefore) / 1024 / 1024; // MB

            $this->perfResults['Memory Usage (100 renders)']['memory'] = $memoryUsed;

            // Memory usage should be reasonable (< 50MB for 100 renders with test overhead)
            expect($memoryUsed)->toBeLessThan(50.0);
        });
    });

    describe('Throughput Benchmarks', function () {
        it('benchmarks rendering throughput with caching', function () {
            // Create template
            $content = '<html><body>' . str_repeat('<p>Paragraph content</p>', 50) . '</body></html>';
            $filePath = $this->testDir . '/pages/throughput.view.php';
            $dir = dirname($filePath);
            if (!is_dir($dir)) mkdir($dir, 0755, true);
            file_put_contents($filePath, $content);

            $context = new TemplateContext(template: 'pages/throughput', data: []);

            // Simulate cache hits
            $cacheKey = CacheKey::fromString('page:pages/throughput:' . md5(serialize([])));
            $cacheItem = CacheItem::hit($cacheKey, $content);
            $this->cache->shouldReceive('get')->andReturn(CacheResult::fromItems($cacheItem));

            // Measure throughput
            $iterations = 1000;
            $start = microtime(true);

            for ($i = 0; $i < $iterations; $i++) {
                $this->cacheManager->render($context, fn() => $content);
            }

            $duration = microtime(true) - $start;
            $throughput = $iterations / $duration;

            $this->perfResults['Throughput (cached)']['throughput_per_sec'] = $throughput;

            echo "\nThroughput: " . number_format($throughput, 0) . " renders/sec\n";

            // Info-gathering: verify reasonable throughput (> 500 renders/sec)
            expect($throughput)->toBeGreaterThan(500);
        });

        it('benchmarks rendering throughput without caching', function () {
            // Create template
            $content = '<html><body>' . str_repeat('<p>Paragraph content</p>', 50) . '</body></html>';
            $filePath = $this->testDir . '/pages/throughput2.view.php';
            $dir = dirname($filePath);
            if (!is_dir($dir)) mkdir($dir, 0755, true);
            file_put_contents($filePath, $content);

            $context = new TemplateContext(template: 'pages/throughput2', data: []);

            // Simulate cache misses
            $this->cache->shouldReceive('get')->andReturn(CacheResult::fromItems(CacheItem::miss(CacheKey::fromString('test'))));
            $this->cache->shouldReceive('set')->andReturn(true);

            // Measure throughput
            $iterations = 100; // Fewer iterations for uncached
            $start = microtime(true);

            for ($i = 0; $i < $iterations; $i++) {
                $this->cacheManager->render($context, fn() => $content);
            }

            $duration = microtime(true) - $start;
            $throughput = $iterations / $duration;

            $this->perfResults['Throughput (uncached)']['throughput_per_sec'] = $throughput;

            echo "\nThroughput (uncached): " . number_format($throughput, 0) . " renders/sec\n";

            // Info-gathering: verify reasonable throughput (> 100 renders/sec)
            expect($throughput)->toBeGreaterThan(100);
        });
    });

    describe('Concurrent Load Simulation', function () {
        it('benchmarks performance under concurrent load', function () {
            // Create template
            $content = '<html><body>Concurrent test content</body></html>';
            $filePath = $this->testDir . '/pages/concurrent.view.php';
            $dir = dirname($filePath);
            if (!is_dir($dir)) mkdir($dir, 0755, true);
            file_put_contents($filePath, $content);

            $context = new TemplateContext(template: 'pages/concurrent', data: []);

            // Simulate cache hits
            $cacheKey = CacheKey::fromString('page:pages/concurrent:' . md5(serialize([])));
            $cacheItem = CacheItem::hit($cacheKey, $content);
            $this->cache->shouldReceive('get')->andReturn(CacheResult::fromItems($cacheItem));

            // Simulate 10 concurrent requests with 100 renders each
            $measurements = [];
            for ($request = 0; $request < 10; $request++) {
                $requestMeasurements = [];
                for ($i = 0; $i < 100; $i++) {
                    $start = microtime(true);
                    $this->cacheManager->render($context, fn() => $content);
                    $requestMeasurements[] = (microtime(true) - $start) * 1000;
                }
                $measurements = array_merge($measurements, $requestMeasurements);
            }

            $stats = calculateStats($measurements);
            $this->perfResults['Concurrent Load (10 requests)'] = $stats;

            // Performance should remain stable under load
            expect($stats['avg'])->toBeLessThan(5.0); // Average < 5ms (with test overhead)
            expect($stats['p99'])->toBeLessThan(100.0); // 99% < 100ms
        });
    });
});

// Helper function for statistics calculation
function calculateStats(array $measurements): array
{
    sort($measurements);
    $count = count($measurements);

    return [
        'count' => $count,
        'min' => min($measurements),
        'max' => max($measurements),
        'avg' => array_sum($measurements) / $count,
        'median' => $measurements[(int)($count / 2)],
        'p95' => $measurements[(int)($count * 0.95)],
        'p99' => $measurements[(int)($count * 0.99)],
        'stddev' => sqrt(array_sum(array_map(fn($x) => pow($x - (array_sum($measurements) / $count), 2), $measurements)) / $count)
    ];
}
