<?php

declare(strict_types=1);

use App\Application\LiveComponents\LiveComponentStateManager;
use App\Application\LiveComponents\ShoppingCart\ShoppingCartState;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\LiveComponents\ValueObjects\ComponentId;
use App\Framework\StateManagement\InMemoryStateManager;
use App\Framework\StateManagement\StateManagerStatistics;

describe('LiveComponentStateManager', function () {
    beforeEach(function () {
        // Create type-safe StateManager for ShoppingCartState
        $this->stateManager = InMemoryStateManager::for(ShoppingCartState::class);
        $this->manager = new LiveComponentStateManager($this->stateManager);
        $this->componentId = ComponentId::create('shopping-cart', 'user-123');
    });

    it('stores and retrieves component state', function () {
        $cartState = ShoppingCartState::fromArray([
            'items' => [
                ['id' => '1', 'name' => 'Product A', 'price' => 29.99, 'quantity' => 2],
            ],
            'discount_code' => 'SAVE10',
            'discount_percentage' => 10
        ]);

        $this->manager->setComponentState($this->componentId, $cartState);

        $retrieved = $this->manager->getComponentState($this->componentId);

        expect($retrieved)->toBeInstanceOf(ShoppingCartState::class);
        expect($retrieved->discountCode)->toBe('SAVE10');
        expect($retrieved->discountPercentage)->toBe(10);
        expect(count($retrieved->items))->toBe(1);
    });

    it('preserves type safety throughout', function () {
        $cartState = ShoppingCartState::fromArray([
            'items' => [
                ['id' => '1', 'name' => 'Product A', 'price' => 19.99, 'quantity' => 1],
            ],
            'discount_code' => 'TEST',
            'discount_percentage' => 5
        ]);

        $this->manager->setComponentState($this->componentId, $cartState);

        $retrieved = $this->manager->getComponentState($this->componentId);

        // Type is preserved - not mixed or LiveComponentState, but ShoppingCartState
        expect($retrieved)->toBeInstanceOf(ShoppingCartState::class);
        expect($retrieved->items)->toBeArray();
        expect($retrieved->discountCode)->toBeString();
    });

    it('checks if component state exists', function () {
        expect($this->manager->hasComponentState($this->componentId))->toBeFalse();

        $cartState = ShoppingCartState::fromArray([
            'items' => [],
            'discount_code' => null,
            'discount_percentage' => 0
        ]);
        $this->manager->setComponentState($this->componentId, $cartState);

        expect($this->manager->hasComponentState($this->componentId))->toBeTrue();
    });

    it('removes component state', function () {
        $cartState = ShoppingCartState::fromArray([
            'items' => [],
            'discount_code' => null,
            'discount_percentage' => 0
        ]);
        $this->manager->setComponentState($this->componentId, $cartState);

        expect($this->manager->hasComponentState($this->componentId))->toBeTrue();

        $this->manager->removeComponentState($this->componentId);

        expect($this->manager->hasComponentState($this->componentId))->toBeFalse();
        expect($this->manager->getComponentState($this->componentId))->toBeNull();
    });

    it('atomically updates component state', function () {
        $initialCart = ShoppingCartState::fromArray([
            'items' => [
                ['id' => '1', 'name' => 'Product A', 'price' => 29.99, 'quantity' => 1],
            ],
            'discount_code' => 'SAVE10',
            'discount_percentage' => 10
        ]);
        $this->manager->setComponentState($this->componentId, $initialCart);

        $updatedCart = $this->manager->updateComponentState(
            $this->componentId,
            function ($state) {
                return $state ? $state->withDiscountCode('SAVE20', 20) : ShoppingCartState::fromArray([]);
            }
        );

        expect($updatedCart)->toBeInstanceOf(ShoppingCartState::class);
        expect($updatedCart->discountCode)->toBe('SAVE20');
        expect($updatedCart->discountPercentage)->toBe(20);
        expect(count($updatedCart->items))->toBe(1);
    });

    it('handles null state in atomic update', function () {
        $newCart = $this->manager->updateComponentState(
            $this->componentId,
            function ($state) {
                return $state ?? ShoppingCartState::fromArray([
                    'items' => [
                        ['id' => '2', 'name' => 'New Product', 'price' => 15.50, 'quantity' => 1],
                    ],
                    'discount_code' => 'FIRST',
                    'discount_percentage' => 5
                ]);
            }
        );

        expect($newCart)->toBeInstanceOf(ShoppingCartState::class);
        expect($newCart->discountCode)->toBe('FIRST');
        expect(count($newCart->items))->toBe(1);
    });

    it('respects TTL for state expiration', function () {
        $cartState = ShoppingCartState::fromArray([
            'items' => [],
            'discount_code' => null,
            'discount_percentage' => 0
        ]);

        // Store with very short TTL
        $this->manager->setComponentState(
            $this->componentId,
            $cartState,
            Duration::fromSeconds(1)
        );

        expect($this->manager->hasComponentState($this->componentId))->toBeTrue();

        // Wait for expiration
        sleep(2);

        // State should be expired (for TTL-aware implementations)
        // Note: InMemoryStateManager doesn't enforce TTL, but the interface supports it
    });

    it('retrieves all component states', function () {
        $cart1 = ShoppingCartState::fromArray([
            'items' => [
                ['id' => '1', 'name' => 'Product A', 'price' => 29.99, 'quantity' => 1],
            ],
            'discount_code' => 'SAVE10',
            'discount_percentage' => 10
        ]);

        $cart2 = ShoppingCartState::fromArray([
            'items' => [
                ['id' => '2', 'name' => 'Product B', 'price' => 15.50, 'quantity' => 2],
            ],
            'discount_code' => 'SAVE20',
            'discount_percentage' => 20
        ]);

        $componentId1 = ComponentId::create('shopping-cart', 'user-123');
        $componentId2 = ComponentId::create('shopping-cart', 'user-456');

        $this->manager->setComponentState($componentId1, $cart1);
        $this->manager->setComponentState($componentId2, $cart2);

        $allStates = $this->manager->getAllComponentStates();

        expect($allStates)->toBeArray();
        expect(count($allStates))->toBeGreaterThanOrEqual(2);
    });

    it('clears all component states', function () {
        $cart1 = ShoppingCartState::fromArray([
            'items' => [],
            'discount_code' => null,
            'discount_percentage' => 0
        ]);

        $cart2 = ShoppingCartState::fromArray([
            'items' => [],
            'discount_code' => 'TEST',
            'discount_percentage' => 5
        ]);

        $componentId1 = ComponentId::create('shopping-cart', 'user-123');
        $componentId2 = ComponentId::create('shopping-cart', 'user-456');

        $this->manager->setComponentState($componentId1, $cart1);
        $this->manager->setComponentState($componentId2, $cart2);

        $this->manager->clearAllComponentStates();

        expect($this->manager->hasComponentState($componentId1))->toBeFalse();
        expect($this->manager->hasComponentState($componentId2))->toBeFalse();
    });

    it('provides statistics about state operations', function () {
        $cartState = ShoppingCartState::fromArray([
            'items' => [],
            'discount_code' => null,
            'discount_percentage' => 0
        ]);

        $this->manager->setComponentState($this->componentId, $cartState);
        $this->manager->getComponentState($this->componentId);
        $this->manager->hasComponentState($this->componentId);

        $stats = $this->manager->getStatistics();

        expect($stats)->toBeInstanceOf(StateManagerStatistics::class);
        expect($stats->totalKeys)->toBeGreaterThanOrEqual(1);
        expect($stats->setCount)->toBeGreaterThanOrEqual(1);
        expect($stats->hitCount)->toBeGreaterThanOrEqual(1);
    });

    it('can be created via factory method', function () {
        $stateManager = InMemoryStateManager::for(ShoppingCartState::class);
        $manager = LiveComponentStateManager::from($stateManager);

        expect($manager)->toBeInstanceOf(LiveComponentStateManager::class);
    });

    it('generates consistent cache keys', function () {
        $componentId1 = ComponentId::fromString('shopping-cart:user-123');
        $componentId2 = ComponentId::fromString('shopping-cart:user-123');
        $componentId3 = ComponentId::fromString('shopping-cart:user-456');

        $cartState = ShoppingCartState::fromArray([
            'items' => [],
            'discount_code' => null,
            'discount_percentage' => 0
        ]);

        $this->manager->setComponentState($componentId1, $cartState);

        // Same component ID should retrieve same state
        expect($this->manager->hasComponentState($componentId2))->toBeTrue();

        // Different instance ID should not have state
        expect($this->manager->hasComponentState($componentId3))->toBeFalse();
    });

    it('handles complex state transformations', function () {
        $initialCart = ShoppingCartState::fromArray([
            'items' => [
                ['id' => '1', 'name' => 'Product A', 'price' => 29.99, 'quantity' => 1],
            ],
            'discount_code' => null,
            'discount_percentage' => 0
        ]);

        $this->manager->setComponentState($this->componentId, $initialCart);

        // Multiple transformations
        $updatedCart = $this->manager->updateComponentState(
            $this->componentId,
            fn($state) => $state->withDiscountCode('SAVE10', 10)
        );

        $updatedCart = $this->manager->updateComponentState(
            $this->componentId,
            function($state) {
                return ShoppingCartState::fromArray([
                    'items' => array_merge($state->items, [
                        ['id' => '2', 'name' => 'Product B', 'price' => 15.50, 'quantity' => 2]
                    ]),
                    'discount_code' => $state->discountCode,
                    'discount_percentage' => $state->discountPercentage
                ]);
            }
        );

        expect(count($updatedCart->items))->toBe(2);
        expect($updatedCart->discountCode)->toBe('SAVE10');
    });
});
