<?php

declare(strict_types=1);

use App\Framework\LiveComponents\Contracts\LiveComponent;
use App\Framework\LiveComponents\Rendering\FragmentRenderer;
use App\Framework\LiveComponents\ValueObjects\ComponentData;
use App\Framework\LiveComponents\ValueObjects\ComponentId;
use App\Framework\View\TemplateRenderer;

describe('FragmentRenderer', function () {
    beforeEach(function () {
        $this->templateRenderer = Mockery::mock(TemplateRenderer::class);
        $this->fragmentRenderer = new FragmentRenderer($this->templateRenderer);
    });

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

    it('renders single fragment from template', function () {
        $component = Mockery::mock(LiveComponent::class);
        $component->shouldReceive('getId')->andReturn(ComponentId::fromString('counter:demo'));
        $component->shouldReceive('getData')->andReturn(ComponentData::fromArray(['count' => 5]));
        $component->shouldReceive('getTemplateName')->andReturn('livecomponent-counter');

        $fullHtml = <<<HTML
        <div id="counter">
            <div data-fragment="counter-display">
                <span>Count: 5</span>
            </div>
            <div data-fragment="counter-controls">
                <button>Increment</button>
            </div>
        </div>
        HTML;

        $this->templateRenderer
            ->shouldReceive('render')
            ->once()
            ->with('livecomponent-counter', ['count' => 5])
            ->andReturn($fullHtml);

        $fragments = $this->fragmentRenderer->renderFragments(
            $component,
            ['counter-display']
        );

        expect($fragments->isEmpty())->toBeFalse();
        expect($fragments->has('counter-display'))->toBeTrue();

        $displayFragment = $fragments->get('counter-display');
        expect($displayFragment)->toContain('Count: 5');
        expect($displayFragment)->toContain('span');
    });

    it('renders multiple fragments from template', function () {
        $component = Mockery::mock(LiveComponent::class);
        $component->shouldReceive('getId')->andReturn(ComponentId::fromString('counter:demo'));
        $component->shouldReceive('getData')->andReturn(ComponentData::fromArray(['count' => 10]));
        $component->shouldReceive('getTemplateName')->andReturn('livecomponent-counter');

        $fullHtml = <<<HTML
        <div id="counter">
            <div data-fragment="counter-display">
                <span>Count: 10</span>
            </div>
            <div data-fragment="counter-controls">
                <button>Increment</button>
                <button>Decrement</button>
            </div>
        </div>
        HTML;

        $this->templateRenderer
            ->shouldReceive('render')
            ->once()
            ->andReturn($fullHtml);

        $fragments = $this->fragmentRenderer->renderFragments(
            $component,
            ['counter-display', 'counter-controls']
        );

        expect($fragments->count())->toBe(2);
        expect($fragments->has('counter-display'))->toBeTrue();
        expect($fragments->has('counter-controls'))->toBeTrue();

        expect($fragments->get('counter-display'))->toContain('Count: 10');
        expect($fragments->get('counter-controls'))->toContain('Increment');
        expect($fragments->get('counter-controls'))->toContain('Decrement');
    });

    it('returns empty collection when fragments not found', function () {
        $component = Mockery::mock(LiveComponent::class);
        $component->shouldReceive('getId')->andReturn(ComponentId::fromString('counter:demo'));
        $component->shouldReceive('getData')->andReturn(ComponentData::fromArray([]));
        $component->shouldReceive('getTemplateName')->andReturn('livecomponent-counter');

        $fullHtml = '<div id="counter"><span>No fragments</span></div>';

        $this->templateRenderer
            ->shouldReceive('render')
            ->once()
            ->andReturn($fullHtml);

        $fragments = $this->fragmentRenderer->renderFragments(
            $component,
            ['non-existent-fragment']
        );

        expect($fragments->isEmpty())->toBeTrue();
    });

    it('handles nested fragments correctly', function () {
        $component = Mockery::mock(LiveComponent::class);
        $component->shouldReceive('getId')->andReturn(ComponentId::fromString('form:demo'));
        $component->shouldReceive('getData')->andReturn(ComponentData::fromArray([]));
        $component->shouldReceive('getTemplateName')->andReturn('livecomponent-form');

        $fullHtml = <<<HTML
        <form data-fragment="form-wrapper">
            <div data-fragment="form-fields">
                <input type="text" name="email" />
                <div data-fragment="form-errors">
                    <span class="error">Invalid email</span>
                </div>
            </div>
        </form>
        HTML;

        $this->templateRenderer
            ->shouldReceive('render')
            ->once()
            ->andReturn($fullHtml);

        $fragments = $this->fragmentRenderer->renderFragments(
            $component,
            ['form-fields', 'form-errors']
        );

        expect($fragments->count())->toBe(2);
        expect($fragments->has('form-fields'))->toBeTrue();
        expect($fragments->has('form-errors'))->toBeTrue();

        // Nested fragment should still be extractable
        expect($fragments->get('form-errors'))->toContain('Invalid email');
    });

    it('preserves HTML structure in fragments', function () {
        $component = Mockery::mock(LiveComponent::class);
        $component->shouldReceive('getId')->andReturn(ComponentId::fromString('card:demo'));
        $component->shouldReceive('getData')->andReturn(ComponentData::fromArray(['title' => 'Test Card']));
        $component->shouldReceive('getTemplateName')->andReturn('livecomponent-card');

        $fullHtml = <<<HTML
        <div class="card">
            <div data-fragment="card-header" class="card-header">
                <h2>Test Card</h2>
                <span class="badge">New</span>
            </div>
            <div data-fragment="card-body">
                <p>Card content</p>
            </div>
        </div>
        HTML;

        $this->templateRenderer
            ->shouldReceive('render')
            ->once()
            ->andReturn($fullHtml);

        $fragments = $this->fragmentRenderer->renderFragments(
            $component,
            ['card-header']
        );

        $header = $fragments->get('card-header');

        // Should preserve attributes and nested structure
        expect($header)->toContain('class="card-header"');
        expect($header)->toContain('<h2>Test Card</h2>');
        expect($header)->toContain('class="badge"');
    });

    it('handles empty fragment list', function () {
        $component = Mockery::mock(LiveComponent::class);
        $component->shouldReceive('getId')->andReturn(ComponentId::fromString('test:demo'));
        $component->shouldReceive('getData')->andReturn(ComponentData::fromArray([]));
        $component->shouldReceive('getTemplateName')->andReturn('livecomponent-test');

        // Should not call renderer if no fragments requested
        $this->templateRenderer->shouldNotReceive('render');

        $fragments = $this->fragmentRenderer->renderFragments($component, []);

        expect($fragments->isEmpty())->toBeTrue();
    });
});
