<?php

declare(strict_types=1);

use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Http\Status;
use App\Framework\LiveComponents\ValueObjects\CacheConfig;

describe('Component Caching Integration', function () {
    it('caches component render output', function () {
        $config = new CacheConfig(
            enabled: true,
            ttl: Duration::fromMinutes(5)
        );

        // First request - cache miss
        $response1 = $this->post('/live-component/stats:user-123', [
            'method' => 'refresh',
            'state' => ['views' => 100],
            'cache_config' => $config->toArray(),
        ]);

        expect($response1->status)->toBe(Status::OK);
        $html1 = $response1->jsonData['html'];

        // Second request - should hit cache
        $response2 = $this->post('/live-component/stats:user-123', [
            'method' => 'refresh',
            'state' => ['views' => 100],
            'cache_config' => $config->toArray(),
        ]);

        expect($response2->status)->toBe(Status::OK);
        $html2 = $response2->jsonData['html'];

        // HTML should be identical (from cache)
        expect($html2)->toBe($html1);
    });

    it('varies cache by specified parameters', function () {
        $config = new CacheConfig(
            enabled: true,
            ttl: Duration::fromMinutes(10),
            varyBy: ['category', 'page']
        );

        // Request with category=electronics, page=1
        $response1 = $this->post('/live-component/products:filter', [
            'method' => 'filter',
            'state' => [
                'category' => 'electronics',
                'page' => 1,
                'results' => [],
            ],
            'cache_config' => $config->toArray(),
        ]);

        // Request with category=electronics, page=2 (different page)
        $response2 = $this->post('/live-component/products:filter', [
            'method' => 'filter',
            'state' => [
                'category' => 'electronics',
                'page' => 2,
                'results' => [],
            ],
            'cache_config' => $config->toArray(),
        ]);

        // Should be different results (different cache keys)
        expect($response1->jsonData['html'])->not->toBe($response2->jsonData['html']);

        // Request with category=books, page=1 (different category)
        $response3 = $this->post('/live-component/products:filter', [
            'method' => 'filter',
            'state' => [
                'category' => 'books',
                'page' => 1,
                'results' => [],
            ],
            'cache_config' => $config->toArray(),
        ]);

        // Should be different from electronics
        expect($response3->jsonData['html'])->not->toBe($response1->jsonData['html']);
    });

    it('supports stale-while-revalidate pattern', function () {
        $config = new CacheConfig(
            enabled: true,
            ttl: Duration::fromSeconds(1), // Short TTL
            staleWhileRevalidate: true,
            staleWhileRevalidateTtl: Duration::fromMinutes(10) // Long SWR TTL
        );

        // First request - cache miss
        $response1 = $this->post('/live-component/news:feed', [
            'method' => 'refresh',
            'state' => ['items' => []],
            'cache_config' => $config->toArray(),
        ]);

        expect($response1->status)->toBe(Status::OK);

        // Wait for TTL to expire (but within SWR window)
        sleep(2);

        // Second request - should serve stale content
        $response2 = $this->post('/live-component/news:feed', [
            'method' => 'refresh',
            'state' => ['items' => []],
            'cache_config' => $config->toArray(),
        ]);

        expect($response2->status)->toBe(Status::OK);

        // Should have cache-control header indicating stale
        if (isset($response2->headers['Cache-Control'])) {
            expect($response2->headers['Cache-Control'])->toContain('stale-while-revalidate');
        }
    });

    it('invalidates cache on component update', function () {
        $config = new CacheConfig(
            enabled: true,
            ttl: Duration::fromMinutes(5)
        );

        // First render - cache
        $response1 = $this->post('/live-component/counter:demo', [
            'method' => 'increment',
            'state' => ['count' => 0],
            'cache_config' => $config->toArray(),
        ]);

        $count1 = $response1->jsonData['state']['count'];

        // Update action - should invalidate cache
        $response2 = $this->post('/live-component/counter:demo', [
            'method' => 'increment',
            'params' => ['amount' => 10],
            'state' => ['count' => $count1],
            'cache_config' => $config->toArray(),
        ]);

        $count2 = $response2->jsonData['state']['count'];

        // Count should have incremented (cache invalidated)
        expect($count2)->toBeGreaterThan($count1);
    });

    it('respects cache disabled config', function () {
        $config = CacheConfig::disabled();

        // First request
        $response1 = $this->post('/live-component/realtime:feed', [
            'method' => 'refresh',
            'state' => ['timestamp' => time()],
            'cache_config' => $config->toArray(),
        ]);

        $timestamp1 = $response1->jsonData['state']['timestamp'];

        sleep(1);

        // Second request - should not use cache
        $response2 = $this->post('/live-component/realtime:feed', [
            'method' => 'refresh',
            'state' => ['timestamp' => time()],
            'cache_config' => $config->toArray(),
        ]);

        $timestamp2 = $response2->jsonData['state']['timestamp'];

        // Timestamps should be different (cache disabled)
        expect($timestamp2)->toBeGreaterThanOrEqual($timestamp1);
    });

    it('caches fragments separately from full render', function () {
        $config = new CacheConfig(
            enabled: true,
            ttl: Duration::fromMinutes(5),
            varyBy: ['fragments']
        );

        // Request full render
        $fullResponse = $this->post('/live-component/card:demo', [
            'method' => 'refresh',
            'state' => ['title' => 'Card Title', 'content' => 'Card Content'],
            'cache_config' => $config->toArray(),
        ]);

        // Request fragment render
        $fragmentResponse = $this->post('/live-component/card:demo', [
            'method' => 'refresh',
            'state' => ['title' => 'Card Title', 'content' => 'Card Content'],
            'fragments' => ['card-header'],
            'cache_config' => $config->toArray(),
        ]);

        // Should cache both independently
        expect($fullResponse->jsonData)->toHaveKey('html');
        expect($fragmentResponse->jsonData)->toHaveKey('fragments');
    });

    it('handles cache for concurrent requests', function () {
        $config = new CacheConfig(
            enabled: true,
            ttl: Duration::fromMinutes(5)
        );

        // Make multiple concurrent requests
        $responses = [];
        for ($i = 0; $i < 5; $i++) {
            $responses[] = $this->post('/live-component/stats:global', [
                'method' => 'refresh',
                'state' => ['total_users' => 1000],
                'cache_config' => $config->toArray(),
            ]);
        }

        // All should succeed
        foreach ($responses as $response) {
            expect($response->status)->toBe(Status::OK);
        }

        // All should have same HTML (from cache)
        $firstHtml = $responses[0]->jsonData['html'];
        foreach (array_slice($responses, 1) as $response) {
            expect($response->jsonData['html'])->toBe($firstHtml);
        }
    });
});
