<?php

declare(strict_types=1);

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

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

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

    describe('shouldCache()', function () {
        it('returns true for templates with "component" in name', function () {
            $context = new TemplateContext(
                template: 'components/button',
                data: ['text' => 'Click me']
            );

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

        it('returns true for templates with "partial" in name', function () {
            $context = new TemplateContext(
                template: 'partials/navigation',
                data: ['items' => []]
            );

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

        it('returns true for nested component paths', function () {
            $context = new TemplateContext(
                template: 'views/components/card/header',
                data: ['title' => 'Header']
            );

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

        it('returns true for partial in subdirectory', function () {
            $context = new TemplateContext(
                template: 'admin/partials/sidebar',
                data: []
            );

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

        it('returns false for regular templates', function () {
            $context = new TemplateContext(
                template: 'pages/home',
                data: []
            );

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

        it('returns false for layouts', function () {
            $context = new TemplateContext(
                template: 'layouts/main',
                data: []
            );

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

        it('is case sensitive for component keyword', function () {
            $context = new TemplateContext(
                template: 'COMPONENTS/button', // Uppercase
                data: []
            );

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

    describe('generateKey()', function () {
        it('generates consistent keys for same component and data', function () {
            $context = new TemplateContext(
                template: 'components/button',
                data: ['text' => 'Click', 'variant' => 'primary']
            );

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

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

        it('generates different keys for different components', function () {
            $context1 = new TemplateContext(
                template: 'components/button',
                data: ['text' => 'Click']
            );
            $context2 = new TemplateContext(
                template: 'components/link',
                data: ['text' => 'Click']
            );

            $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: 'components/button',
                data: ['text' => 'Submit']
            );
            $context2 = new TemplateContext(
                template: 'components/button',
                data: ['text' => 'Cancel']
            );

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

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

        it('uses basename for nested component paths', function () {
            $context1 = new TemplateContext(
                template: 'views/components/button',
                data: ['text' => 'Click']
            );
            $context2 = new TemplateContext(
                template: 'other/components/button',
                data: ['text' => 'Click']
            );

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

            // Same basename and data = same key
            expect((string) $key1)->toBe((string) $key2);
        });

        it('includes component basename in cache key', function () {
            $context = new TemplateContext(
                template: 'components/alert',
                data: ['message' => 'Success']
            );

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

            expect((string) $key)->toContain('component:alert:');
        });

        it('includes data hash in cache key', function () {
            $context = new TemplateContext(
                template: 'components/card',
                data: ['title' => 'Test', 'content' => 'Content']
            );

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

            // Key should have format: component:card:{hash}
            expect($keyString)->toStartWith('component:card:');
            expect(strlen($keyString))->toBeGreaterThan(strlen('component:card:'));
        });

        it('generates same key for identical nested data structures', function () {
            $data = [
                'user' => ['name' => 'John', 'role' => 'admin'],
                'settings' => ['theme' => 'dark']
            ];

            $context1 = new TemplateContext(
                template: 'components/profile',
                data: $data
            );
            $context2 = new TemplateContext(
                template: 'components/profile',
                data: $data
            );

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

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

    describe('getTtl()', function () {
        it('returns 1800 seconds for all components', function () {
            $context = new TemplateContext(
                template: 'components/button',
                data: []
            );

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

        it('returns consistent TTL for different components', function () {
            $context1 = new TemplateContext(
                template: 'components/card',
                data: []
            );
            $context2 = new TemplateContext(
                template: 'partials/header',
                data: []
            );

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

        it('returns consistent TTL regardless of data', function () {
            $context1 = new TemplateContext(
                template: 'components/button',
                data: ['small' => 'data']
            );
            $context2 = new TemplateContext(
                template: 'components/button',
                data: ['large' => str_repeat('x', 1000)]
            );

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

    describe('canInvalidate()', function () {
        it('returns true for component templates', function () {
            expect($this->strategy->canInvalidate('components/button'))->toBeTrue();
        });

        it('returns true for partial templates', function () {
            expect($this->strategy->canInvalidate('partials/navigation'))->toBeTrue();
        });

        it('returns true for nested component paths', function () {
            expect($this->strategy->canInvalidate('views/components/card'))->toBeTrue();
        });

        it('returns false for regular templates', function () {
            expect($this->strategy->canInvalidate('pages/home'))->toBeFalse();
        });

        it('returns false for layouts', function () {
            expect($this->strategy->canInvalidate('layouts/main'))->toBeFalse();
        });
    });

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

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

            $key = $this->strategy->generateKey($context);
            expect($key)->toBeInstanceOf(CacheKey::class);
        });

        it('handles complex nested data structures', function () {
            $context = new TemplateContext(
                template: 'components/table',
                data: [
                    'headers' => ['Name', 'Email', 'Role'],
                    'rows' => [
                        ['John Doe', 'john@example.com', 'admin'],
                        ['Jane Smith', 'jane@example.com', 'user']
                    ],
                    'pagination' => [
                        'page' => 1,
                        'total' => 10,
                        'perPage' => 20
                    ]
                ]
            );

            $key = $this->strategy->generateKey($context);
            expect($key)->toBeInstanceOf(CacheKey::class);
            expect((string) $key)->toBeString();
            expect(strlen((string) $key))->toBeGreaterThan(0);
        });

        it('handles special characters in component name', function () {
            $context = new TemplateContext(
                template: 'components/user-profile_card',
                data: ['user_id' => 123]
            );

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

            $key = $this->strategy->generateKey($context);
            expect((string) $key)->toContain('component:user-profile_card:');
        });

        it('treats "component" and "partial" equally for caching', function () {
            $componentContext = new TemplateContext(
                template: 'components/item',
                data: ['id' => 1]
            );
            $partialContext = new TemplateContext(
                template: 'partials/item',
                data: ['id' => 1]
            );

            expect($this->strategy->shouldCache($componentContext))->toBeTrue();
            expect($this->strategy->shouldCache($partialContext))->toBeTrue();
            expect($this->strategy->getTtl($componentContext))->toBe(
                $this->strategy->getTtl($partialContext)
            );
        });

        it('handles component path with multiple segments', function () {
            $context = new TemplateContext(
                template: 'admin/dashboard/components/widget/chart',
                data: ['data' => [1, 2, 3]]
            );

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

            $key = $this->strategy->generateKey($context);
            // Basename should be 'chart'
            expect((string) $key)->toContain('component:chart:');
        });

        it('generates valid cache key from data with objects', function () {
            $context = new TemplateContext(
                template: 'components/user-card',
                data: [
                    'user' => (object) ['name' => 'John', 'email' => 'john@example.com'],
                    'timestamp' => new \DateTimeImmutable('2024-01-01 12:00:00')
                ]
            );

            $key = $this->strategy->generateKey($context);
            expect($key)->toBeInstanceOf(CacheKey::class);
            expect((string) $key)->toStartWith('component:user-card:');
        });
    });
});
