<?php

declare(strict_types=1);

use App\Framework\Cache\Cache;
use App\Framework\Cache\CacheKey;
use App\Framework\View\Caching\Strategies\FullPageCacheStrategy;
use App\Framework\View\Caching\TemplateContext;

describe('FullPageCacheStrategy', function () {
    beforeEach(function () {
        $this->cache = Mockery::mock(Cache::class);
        $this->strategy = new FullPageCacheStrategy($this->cache);
    });

    afterEach(function () {
        Mockery::close();
    });

    describe('shouldCache()', function () {
        it('returns true for static content without user data', function () {
            $context = new TemplateContext(
                template: 'static-page',
                data: ['title' => 'Welcome', 'content' => 'Hello World']
            );

            expect($this->strategy->shouldCache($context))->toBeTrue();
        });

        it('returns false when user data is present', function () {
            $context = new TemplateContext(
                template: 'user-dashboard',
                data: ['user' => ['name' => 'John'], 'title' => 'Dashboard']
            );

            expect($this->strategy->shouldCache($context))->toBeFalse();
        });

        it('returns false when auth data is present', function () {
            $context = new TemplateContext(
                template: 'profile',
                data: ['auth' => ['authenticated' => true]]
            );

            expect($this->strategy->shouldCache($context))->toBeFalse();
        });

        it('returns false when session data is present', function () {
            $context = new TemplateContext(
                template: 'checkout',
                data: ['session' => ['cart_items' => 3]]
            );

            expect($this->strategy->shouldCache($context))->toBeFalse();
        });

        it('returns false when csrf token is present', function () {
            $context = new TemplateContext(
                template: 'contact-form',
                data: ['csrf_token' => 'abc123']
            );

            expect($this->strategy->shouldCache($context))->toBeFalse();
        });

        it('returns false when flash messages are present', function () {
            $context = new TemplateContext(
                template: 'result-page',
                data: ['flash' => ['success' => 'Saved!']]
            );

            expect($this->strategy->shouldCache($context))->toBeFalse();
        });

        it('returns false when errors are present', function () {
            $context = new TemplateContext(
                template: 'form',
                data: ['errors' => ['email' => 'Invalid email']]
            );

            expect($this->strategy->shouldCache($context))->toBeFalse();
        });

        it('returns false when timestamp is present', function () {
            $context = new TemplateContext(
                template: 'news',
                data: ['timestamp' => time()]
            );

            expect($this->strategy->shouldCache($context))->toBeFalse();
        });

        it('returns true when only non-volatile data is present', function () {
            $context = new TemplateContext(
                template: 'product-page',
                data: [
                    'product' => ['name' => 'Laptop', 'price' => 999],
                    'reviews' => [['rating' => 5]]
                ]
            );

            expect($this->strategy->shouldCache($context))->toBeTrue();
        });
    });

    describe('generateKey()', function () {
        it('generates consistent keys for same template and data', function () {
            $context = new TemplateContext(
                template: 'product',
                data: ['id' => 123, 'name' => 'Laptop']
            );

            $key1 = $this->strategy->generateKey($context);
            $key2 = $this->strategy->generateKey($context);

            expect((string) $key1)->toBe((string) $key2);
        });

        it('generates different keys for different templates', function () {
            $context1 = new TemplateContext(
                template: 'product',
                data: ['id' => 123]
            );
            $context2 = new TemplateContext(
                template: 'category',
                data: ['id' => 123]
            );

            $key1 = $this->strategy->generateKey($context1);
            $key2 = $this->strategy->generateKey($context2);

            expect((string) $key1)->not->toBe((string) $key2);
        });

        it('generates different keys for different data', function () {
            $context1 = new TemplateContext(
                template: 'product',
                data: ['id' => 123]
            );
            $context2 = new TemplateContext(
                template: 'product',
                data: ['id' => 456]
            );

            $key1 = $this->strategy->generateKey($context1);
            $key2 = $this->strategy->generateKey($context2);

            expect((string) $key1)->not->toBe((string) $key2);
        });

        it('generates same key when volatile data changes', function () {
            $context1 = new TemplateContext(
                template: 'page',
                data: ['title' => 'Test', 'csrf_token' => 'abc']
            );
            $context2 = new TemplateContext(
                template: 'page',
                data: ['title' => 'Test', 'csrf_token' => 'xyz']
            );

            $key1 = $this->strategy->generateKey($context1);
            $key2 = $this->strategy->generateKey($context2);

            // CSRF token sollte ignoriert werden
            expect((string) $key1)->toBe((string) $key2);
        });

        it('includes template name in cache key', function () {
            $context = new TemplateContext(
                template: 'about-page',
                data: ['content' => 'About us']
            );

            $key = $this->strategy->generateKey($context);

            expect((string) $key)->toContain('page:about-page:');
        });
    });

    describe('getTtl()', function () {
        it('returns 3600 seconds for layout templates', function () {
            $context = new TemplateContext(
                template: 'layouts/main',
                data: []
            );

            expect($this->strategy->getTtl($context))->toBe(3600);
        });

        it('returns 7200 seconds for static templates', function () {
            $context = new TemplateContext(
                template: 'static/about',
                data: []
            );

            expect($this->strategy->getTtl($context))->toBe(7200);
        });

        it('returns 1800 seconds for content pages', function () {
            $context = new TemplateContext(
                template: 'page/home',
                data: []
            );

            expect($this->strategy->getTtl($context))->toBe(1800);
        });

        it('returns 900 seconds for default templates', function () {
            $context = new TemplateContext(
                template: 'misc-template',
                data: []
            );

            expect($this->strategy->getTtl($context))->toBe(900);
        });
    });

    describe('canInvalidate()', function () {
        it('returns true for all templates', function () {
            expect($this->strategy->canInvalidate('any-template'))->toBeTrue();
            expect($this->strategy->canInvalidate('layout'))->toBeTrue();
            expect($this->strategy->canInvalidate('component'))->toBeTrue();
        });
    });

    describe('edge cases', function () {
        it('handles empty data array', function () {
            $context = new TemplateContext(
                template: 'empty',
                data: []
            );

            expect($this->strategy->shouldCache($context))->toBeTrue();
        });

        it('handles nested user data', function () {
            $context = new TemplateContext(
                template: 'nested',
                data: [
                    'content' => ['text' => 'Hello'],
                    'user' => ['name' => 'John']  // Top-level user key
                ]
            );

            expect($this->strategy->shouldCache($context))->toBeFalse();
        });

        it('handles mixed volatile and non-volatile data', function () {
            $context = new TemplateContext(
                template: 'mixed',
                data: [
                    'product' => ['id' => 1],
                    'flash' => ['message' => 'Success']
                ]
            );

            expect($this->strategy->shouldCache($context))->toBeFalse();
        });

        it('generates valid cache key from complex data', function () {
            $context = new TemplateContext(
                template: 'complex',
                data: [
                    'nested' => ['deep' => ['value' => 123]],
                    'array' => [1, 2, 3],
                    'string' => 'test'
                ]
            );

            $key = $this->strategy->generateKey($context);

            expect($key)->toBeInstanceOf(CacheKey::class);
            expect((string) $key)->toBeString();
            expect(strlen((string) $key))->toBeGreaterThan(0);
        });
    });
});
