<?php

declare(strict_types=1);

use App\Application\LiveComponents\CardComponent;
use App\Application\LiveComponents\ContainerComponent;
use App\Application\LiveComponents\LayoutComponent;
use App\Application\LiveComponents\ModalComponent;
use App\Framework\LiveComponents\SlotManager;
use App\Framework\LiveComponents\ValueObjects\ComponentId;
use App\Framework\LiveComponents\ValueObjects\ComponentState;
use App\Framework\LiveComponents\ValueObjects\SlotContent;

describe('Slot System Integration', function () {
    beforeEach(function () {
        $this->slotManager = new SlotManager();
    });

    describe('CardComponent', function () {
        it('renders with custom header, body and footer slots', function () {
            $component = new CardComponent(
                id: ComponentId::generate(),
                state: ComponentState::fromArray([])
            );

            $providedSlots = [
                SlotContent::named('header', '<h2>User Profile</h2>'),
                SlotContent::named('body', '<p>User details...</p>'),
                SlotContent::named('footer', '<button>Edit</button>'),
            ];

            // Validate slots
            $errors = $this->slotManager->validateSlots($component, $providedSlots);
            expect($errors)->toBeEmpty();

            // Resolve each slot
            $definitions = $component->getSlotDefinitions();

            $headerContent = $this->slotManager->resolveSlotContent(
                $component,
                $definitions[0], // header
                $providedSlots
            );

            $bodyContent = $this->slotManager->resolveSlotContent(
                $component,
                $definitions[1], // body
                $providedSlots
            );

            $footerContent = $this->slotManager->resolveSlotContent(
                $component,
                $definitions[2], // footer
                $providedSlots
            );

            expect($headerContent)->toContain('User Profile');
            expect($bodyContent)->toContain('User details');
            expect($footerContent)->toContain('Edit');
        });

        it('validates that body slot is required', function () {
            $component = new CardComponent(
                id: ComponentId::generate(),
                state: ComponentState::fromArray([])
            );

            // Only provide header, skip required body
            $providedSlots = [
                SlotContent::named('header', '<h2>Header</h2>'),
            ];

            $errors = $this->slotManager->validateSlots($component, $providedSlots);

            expect($errors)->toContain("Required slot 'body' is not filled");
        });

        it('uses default header when not provided', function () {
            $component = new CardComponent(
                id: ComponentId::generate(),
                state: ComponentState::fromArray([])
            );

            $providedSlots = [
                SlotContent::named('body', '<p>Body content</p>'),
            ];

            $definitions = $component->getSlotDefinitions();

            $headerContent = $this->slotManager->resolveSlotContent(
                $component,
                $definitions[0], // header
                $providedSlots
            );

            expect($headerContent)->toContain('card-header-default');
        });
    });

    describe('ModalComponent', function () {
        it('renders with scoped context in content and actions slots', function () {
            $modalId = ComponentId::generate();

            $component = new ModalComponent(
                id: $modalId,
                state: ComponentState::fromArray(['isOpen' => true])
            );

            $providedSlots = [
                SlotContent::named('title', '<h3>Confirm</h3>'),
                SlotContent::named('content', '<p>Modal ID: {context.modalId}</p>'),
                SlotContent::named('actions', '<button onclick="{context.closeFunction}">Close</button>'),
            ];

            $definitions = $component->getSlotDefinitions();

            $contentSlot = $this->slotManager->resolveSlotContent(
                $component,
                $definitions[1], // content (scoped)
                $providedSlots
            );

            $actionsSlot = $this->slotManager->resolveSlotContent(
                $component,
                $definitions[2], // actions (scoped)
                $providedSlots
            );

            // Check that context was injected
            expect($contentSlot)->toContain($modalId->toString());
            expect($actionsSlot)->toContain("closeModal('{$modalId->toString()}')");
        });

        it('validates that content slot is required', function () {
            $component = new ModalComponent(
                id: ComponentId::generate(),
                state: ComponentState::fromArray([])
            );

            // Only provide title, skip required content
            $providedSlots = [
                SlotContent::named('title', '<h3>Title</h3>'),
            ];

            $errors = $this->slotManager->validateSlots($component, $providedSlots);

            expect($errors)->toContain("Required slot 'content' is not filled");
        });
    });

    describe('LayoutComponent', function () {
        it('renders with sidebar, main, header and footer slots', function () {
            $component = new LayoutComponent(
                id: ComponentId::generate(),
                state: ComponentState::fromArray([
                    'sidebarWidth' => '300px',
                    'sidebarCollapsed' => false,
                ])
            );

            $providedSlots = [
                SlotContent::named('header', '<header>App Header</header>'),
                SlotContent::named('sidebar', '<nav>Sidebar (width: {context.sidebarWidth})</nav>'),
                SlotContent::named('main', '<main>Main content</main>'),
                SlotContent::named('footer', '<footer>Footer</footer>'),
            ];

            $errors = $this->slotManager->validateSlots($component, $providedSlots);
            expect($errors)->toBeEmpty();

            $definitions = $component->getSlotDefinitions();

            $sidebarContent = $this->slotManager->resolveSlotContent(
                $component,
                $definitions[0], // sidebar (scoped)
                $providedSlots
            );

            // Check scoped context injection
            expect($sidebarContent)->toContain('300px');
        });

        it('validates that main slot is required', function () {
            $component = new LayoutComponent(
                id: ComponentId::generate(),
                state: ComponentState::fromArray([])
            );

            $providedSlots = [
                SlotContent::named('sidebar', '<nav>Sidebar</nav>'),
            ];

            $errors = $this->slotManager->validateSlots($component, $providedSlots);

            expect($errors)->toContain("Required slot 'main' is not filled");
        });
    });

    describe('ContainerComponent', function () {
        it('renders with default slot and actions', function () {
            $component = new ContainerComponent(
                id: ComponentId::generate(),
                state: ComponentState::fromArray(['padding' => 'large'])
            );

            $providedSlots = [
                SlotContent::default('<h1>Welcome</h1><p>Content</p>'),
                SlotContent::named('title', '<h2>Container</h2>'),
                SlotContent::named('actions', '<button>Save</button>'),
            ];

            $errors = $this->slotManager->validateSlots($component, $providedSlots);
            expect($errors)->toBeEmpty();

            $definitions = $component->getSlotDefinitions();

            $defaultContent = $this->slotManager->resolveSlotContent(
                $component,
                $definitions[0], // default
                $providedSlots
            );

            expect($defaultContent)->toContain('Welcome');
            expect($defaultContent)->toContain('container-padding-large');
        });

        it('uses default content when default slot is not provided', function () {
            $component = new ContainerComponent(
                id: ComponentId::generate(),
                state: ComponentState::fromArray([])
            );

            $providedSlots = [
                SlotContent::named('title', '<h2>Container</h2>'),
            ];

            $definitions = $component->getSlotDefinitions();

            $defaultContent = $this->slotManager->resolveSlotContent(
                $component,
                $definitions[0], // default
                $providedSlots
            );

            expect($defaultContent)->toContain('empty-container');
        });
    });

    describe('Slot Content Processing', function () {
        it('processes slot content through component hook', function () {
            $component = new CardComponent(
                id: ComponentId::generate(),
                state: ComponentState::fromArray([])
            );

            $providedSlots = [
                SlotContent::named('header', '<h2>Header</h2>'),
                SlotContent::named('body', '<p>Body</p>'),
            ];

            $definitions = $component->getSlotDefinitions();

            // Header gets wrapped in div.card-header
            $headerContent = $this->slotManager->resolveSlotContent(
                $component,
                $definitions[0],
                $providedSlots
            );

            expect($headerContent)->toContain('<div class="card-header">');
            expect($headerContent)->toContain('</div>');

            // Body gets wrapped in div.card-body
            $bodyContent = $this->slotManager->resolveSlotContent(
                $component,
                $definitions[1],
                $providedSlots
            );

            expect($bodyContent)->toContain('<div class="card-body">');
        });
    });

    describe('XSS Protection', function () {
        it('escapes HTML in scoped context values', function () {
            $component = new ModalComponent(
                id: ComponentId::generate(),
                state: ComponentState::fromArray([])
            );

            // Try to inject script via slot content
            $providedSlots = [
                SlotContent::named('content', '<p>{context.modalId}</p>'),
            ];

            $definitions = $component->getSlotDefinitions();

            $content = $this->slotManager->resolveSlotContent(
                $component,
                $definitions[1], // content (scoped)
                $providedSlots
            );

            // modalId is a ComponentId, which gets htmlspecialchars treatment
            // Check that HTML entities are properly escaped
            expect($content)->not->toContain('<script>');
        });
    });

    describe('Slot Statistics', function () {
        it('tracks slot registration statistics', function () {
            $componentId1 = ComponentId::generate();
            $componentId2 = ComponentId::generate();

            $this->slotManager->registerSlotContents($componentId1, [
                SlotContent::named('header', '<h1>Header</h1>'),
                SlotContent::named('body', '<p>Body</p>'),
            ]);

            $this->slotManager->registerSlotContents($componentId2, [
                SlotContent::named('footer', '<footer>Footer</footer>'),
            ]);

            $stats = $this->slotManager->getStats();

            expect($stats['total_components_with_slots'])->toBe(2);
            expect($stats['total_slot_contents'])->toBe(3);
            expect($stats['avg_slots_per_component'])->toBe(1.5);
        });
    });
});
