<?php

declare(strict_types=1);

use App\Framework\Http\Status;

describe('Fragment Update Integration', function () {
    it('updates single fragment via action', function () {
        // Create counter component
        $createResponse = $this->post('/live-component/counter:demo/create', [
            'initial_count' => 0,
        ]);

        expect($createResponse->status)->toBe(Status::OK);

        $componentId = $createResponse->jsonData['id'];

        // Request fragment update
        $updateResponse = $this->post("/live-component/{$componentId}", [
            'method' => 'increment',
            'params' => ['amount' => 5],
            'state' => ['count' => 0],
            'fragments' => ['counter-display'],
        ]);

        expect($updateResponse->status)->toBe(Status::OK);

        $data = $updateResponse->jsonData;

        // Should return fragments instead of full HTML
        expect($data)->toHaveKey('fragments');
        expect($data)->not->toHaveKey('html');

        expect($data['fragments'])->toHaveKey('counter-display');
        expect($data['fragments']['counter-display'])->toContain('5');

        // Should still return updated state
        expect($data['state']['count'])->toBe(5);
    });

    it('updates multiple fragments simultaneously', function () {
        $createResponse = $this->post('/live-component/form:demo/create', [
            'email' => '',
            'errors' => [],
        ]);

        $componentId = $createResponse->jsonData['id'];

        // Request multiple fragment updates
        $updateResponse = $this->post("/live-component/{$componentId}", [
            'method' => 'validate',
            'params' => ['email' => 'invalid-email'],
            'state' => ['email' => '', 'errors' => []],
            'fragments' => ['form-input', 'form-errors'],
        ]);

        expect($updateResponse->status)->toBe(Status::OK);

        $data = $updateResponse->jsonData;
        $fragments = $data['fragments'];

        expect($fragments)->toHaveKey('form-input');
        expect($fragments)->toHaveKey('form-errors');

        // Input fragment should reflect new value
        expect($fragments['form-input'])->toContain('invalid-email');

        // Errors fragment should show validation error
        expect($fragments['form-errors'])->toContain('email');
    });

    it('falls back to full render when fragments not found', function () {
        $createResponse = $this->post('/live-component/counter:demo/create', [
            'initial_count' => 0,
        ]);

        $componentId = $createResponse->jsonData['id'];

        // Request non-existent fragment
        $updateResponse = $this->post("/live-component/{$componentId}", [
            'method' => 'increment',
            'params' => ['amount' => 1],
            'state' => ['count' => 0],
            'fragments' => ['non-existent-fragment'],
        ]);

        expect($updateResponse->status)->toBe(Status::OK);

        $data = $updateResponse->jsonData;

        // Should fall back to full HTML
        expect($data)->toHaveKey('html');
        expect($data)->not->toHaveKey('fragments');
    });

    it('preserves events with fragment updates', function () {
        $createResponse = $this->post('/live-component/counter:demo/create', [
            'initial_count' => 0,
        ]);

        $componentId = $createResponse->jsonData['id'];

        $updateResponse = $this->post("/live-component/{$componentId}", [
            'method' => 'increment',
            'params' => ['amount' => 10],
            'state' => ['count' => 0],
            'fragments' => ['counter-display'],
        ]);

        $data = $updateResponse->jsonData;

        // Should include events even with fragment update
        expect($data)->toHaveKey('events');
        expect($data['events'])->toBeArray();

        // If counter dispatches increment event
        if (count($data['events']) > 0) {
            expect($data['events'][0])->toHaveKey('type');
        }
    });

    it('handles empty fragments array as full render', function () {
        $createResponse = $this->post('/live-component/counter:demo/create', [
            'initial_count' => 0,
        ]);

        $componentId = $createResponse->jsonData['id'];

        // Empty fragments array should trigger full render
        $updateResponse = $this->post("/live-component/{$componentId}", [
            'method' => 'increment',
            'params' => ['amount' => 1],
            'state' => ['count' => 0],
            'fragments' => [],
        ]);

        $data = $updateResponse->jsonData;

        // Should return full HTML, not fragments
        expect($data)->toHaveKey('html');
        expect($data)->not->toHaveKey('fragments');
    });

    it('updates fragments with complex nested HTML', function () {
        $createResponse = $this->post('/live-component/card:demo/create', [
            'title' => 'Original Title',
            'content' => 'Original Content',
        ]);

        $componentId = $createResponse->jsonData['id'];

        $updateResponse = $this->post("/live-component/{$componentId}", [
            'method' => 'updateTitle',
            'params' => ['title' => 'New Title'],
            'state' => ['title' => 'Original Title', 'content' => 'Original Content'],
            'fragments' => ['card-header'],
        ]);

        $data = $updateResponse->jsonData;

        if (isset($data['fragments']['card-header'])) {
            $header = $data['fragments']['card-header'];

            // Should contain new title
            expect($header)->toContain('New Title');

            // Should preserve HTML structure
            expect($header)->toMatch('/<div[^>]*data-fragment="card-header"[^>]*>/');
        }
    });

    it('handles concurrent fragment updates independently', function () {
        $create1 = $this->post('/live-component/counter:demo1/create', ['initial_count' => 0]);
        $create2 = $this->post('/live-component/counter:demo2/create', ['initial_count' => 10]);

        $id1 = $create1->jsonData['id'];
        $id2 = $create2->jsonData['id'];

        // Update both concurrently with fragments
        $update1 = $this->post("/live-component/{$id1}", [
            'method' => 'increment',
            'params' => ['amount' => 5],
            'state' => ['count' => 0],
            'fragments' => ['counter-display'],
        ]);

        $update2 = $this->post("/live-component/{$id2}", [
            'method' => 'decrement',
            'params' => ['amount' => 3],
            'state' => ['count' => 10],
            'fragments' => ['counter-display'],
        ]);

        // Both should succeed independently
        expect($update1->status)->toBe(Status::OK);
        expect($update2->status)->toBe(Status::OK);

        expect($update1->jsonData['state']['count'])->toBe(5);
        expect($update2->jsonData['state']['count'])->toBe(7);
    });
});
