<?php

declare(strict_types=1);

require_once __DIR__ . '/../../vendor/autoload.php';

use App\Framework\LiveComponents\ComponentEventDispatcher;
use App\Framework\LiveComponents\ValueObjects\ActionParameters;
use App\Framework\LiveComponents\ValueObjects\ComponentData;
use App\Framework\LiveComponents\ValueObjects\ComponentEvent;
use App\Framework\LiveComponents\ValueObjects\ComponentId;
use App\Framework\LiveComponents\ValueObjects\EventPayload;

echo "Testing LiveComponents Integration with Value Objects\n";
echo "=====================================================\n\n";

$testsPassed = 0;
$testsFailed = 0;

function test(string $name, callable $fn): void
{
    global $testsPassed, $testsFailed;

    try {
        $fn();
        echo "✅ {$name}\n";
        $testsPassed++;
    } catch (Throwable $e) {
        echo "❌ {$name}\n";
        echo "   Error: {$e->getMessage()}\n";
        echo "   File: {$e->getFile()}:{$e->getLine()}\n";
        $testsFailed++;
    }
}

// Test 1: ComponentId creation and usage
test('ComponentId: Create and parse', function () {
    $id = ComponentId::create('chart', 'user-123');
    assert($id->name === 'chart');
    assert($id->instanceId === 'user-123');
    assert($id->toString() === 'chart:user-123');

    $parsed = ComponentId::fromString('chart:user-123');
    assert($parsed->equals($id));
});

// Test 2: ComponentData immutability
test('ComponentData: Immutable updates', function () {
    $data1 = ComponentData::fromArray(['count' => 0]);
    $data2 = $data1->with('count', 5);

    assert($data1->get('count') === 0); // Original unchanged
    assert($data2->get('count') === 5); // New instance updated
});

// Test 3: ActionParameters type coercion
test('ActionParameters: Type coercion', function () {
    $params = ActionParameters::fromArray([
        'count' => '42',
        'price' => '19.99',
        'active' => '1',
    ]);

    assert($params->getInt('count') === 42);
    assert($params->getFloat('price') === 19.99);
    assert($params->getBool('active') === true);
});

// Test 4: EventPayload creation and access
test('EventPayload: Create and access', function () {
    $payload = EventPayload::fromArray([
        'user_id' => 123,
        'action' => 'clicked',
        'timestamp' => time(),
    ]);

    assert($payload->getInt('user_id') === 123);
    assert($payload->getString('action') === 'clicked');
    assert($payload->has('timestamp'));
});

// Test 5: ComponentEvent broadcast
test('ComponentEvent: Broadcast event', function () {
    $payload = EventPayload::fromArray(['value' => 42]);
    $event = ComponentEvent::broadcast('counter:updated', $payload);

    assert($event->name === 'counter:updated');
    assert($event->isBroadcast());
    assert(! $event->isTargeted());
    assert($event->payload->getInt('value') === 42);
});

// Test 6: ComponentEvent targeted
test('ComponentEvent: Targeted event', function () {
    $payload = EventPayload::fromArray(['message' => 'Hello']);
    $event = ComponentEvent::target('notification:show', 'notification:instance-1', $payload);

    assert($event->name === 'notification:show');
    assert($event->isTargeted());
    assert(! $event->isBroadcast());
    assert($event->targetsComponent('notification:instance-1'));
    assert($event->payload->getString('message') === 'Hello');
});

// Test 7: ComponentEventDispatcher
test('ComponentEventDispatcher: Dispatch events', function () {
    $dispatcher = new ComponentEventDispatcher();

    $payload1 = EventPayload::fromArray(['count' => 1]);
    $dispatcher->dispatch('event1', $payload1);

    $payload2 = EventPayload::fromArray(['count' => 2]);
    $dispatcher->dispatchTo('event2', 'target:123', $payload2);

    assert($dispatcher->hasEvents());
    $events = $dispatcher->getEvents();
    assert(count($events) === 2);

    assert($events[0]->name === 'event1');
    assert($events[0]->isBroadcast());

    assert($events[1]->name === 'event2');
    assert($events[1]->isTargeted());
});

// Test 8: Full integration scenario
test('Integration: Complete component lifecycle', function () {
    // 1. Create component identity
    $componentId = ComponentId::create('shopping-cart', 'session-abc');

    // 2. Initialize component data
    $initialData = ComponentData::fromArray([
        'items' => [],
        'total' => 0,
    ]);

    // 3. Simulate action with parameters
    $actionParams = ActionParameters::fromArray([
        'product_id' => 'prod-123',
        'quantity' => 2,
        'price' => 29.99,
    ]);

    // 4. Update component data
    $items = $initialData->get('items', []);
    $items[] = [
        'product_id' => $actionParams->getString('product_id'),
        'quantity' => $actionParams->getInt('quantity'),
        'price' => $actionParams->getFloat('price'),
    ];

    $newData = $initialData
        ->with('items', $items)
        ->with('total', $actionParams->getFloat('price') * $actionParams->getInt('quantity'));

    // 5. Dispatch event
    $dispatcher = new ComponentEventDispatcher();
    $eventPayload = EventPayload::fromArray([
        'item_count' => count($items),
        'total' => $newData->get('total'),
    ]);
    $dispatcher->dispatch('cart:updated', $eventPayload);

    // Assertions
    assert($componentId->toString() === 'shopping-cart:session-abc');
    assert(count($newData->get('items')) === 1);
    assert($newData->get('total') === 59.98);
    assert($dispatcher->hasEvents());

    $events = $dispatcher->getEvents();
    assert($events[0]->name === 'cart:updated');
    assert($events[0]->payload->getInt('item_count') === 1);
});

// Test 9: Empty payload handling
test('EventPayload: Empty payload', function () {
    $event = ComponentEvent::broadcast('notification:clear');
    assert($event->payload->isEmpty());
    assert($event->payload->size() === 0);
});

// Test 10: ComponentData merge
test('ComponentData: Merge operations', function () {
    $data1 = ComponentData::fromArray(['a' => 1, 'b' => 2]);
    $data2 = ComponentData::fromArray(['c' => 3, 'd' => 4]);
    $merged = $data1->merge($data2);

    assert($merged->get('a') === 1);
    assert($merged->get('b') === 2);
    assert($merged->get('c') === 3);
    assert($merged->get('d') === 4);
});

// Test 11: ActionParameters validation
test('ActionParameters: Required parameters', function () {
    $params = ActionParameters::fromArray(['name' => 'John']);

    try {
        $params->requireString('email');
        assert(false, 'Should have thrown exception');
    } catch (InvalidArgumentException $e) {
        assert(str_contains($e->getMessage(), 'missing'));
    }
});

// Test 12: ComponentEvent serialization
test('ComponentEvent: Array serialization', function () {
    $payload = EventPayload::fromArray(['key' => 'value']);
    $event = ComponentEvent::broadcast('test:event', $payload);

    $array = $event->toArray();
    assert($array['name'] === 'test:event');
    assert($array['payload']['key'] === 'value');
    assert($array['target'] === null);
});

// Test 13: ComponentData filtering
test('ComponentData: Only/Except filtering', function () {
    $data = ComponentData::fromArray(['a' => 1, 'b' => 2, 'c' => 3, 'd' => 4]);

    $only = $data->only(['a', 'c']);
    assert($only->size() === 2);
    assert($only->has('a') && $only->has('c'));
    assert(! $only->has('b') && ! $only->has('d'));

    $except = $data->except(['b', 'd']);
    assert($except->size() === 2);
    assert($except->has('a') && $except->has('c'));
    assert(! $except->has('b') && ! $except->has('d'));
});

// Test 14: Type safety enforcement
test('Type Safety: No primitive arrays allowed', function () {
    // ComponentEvent only accepts EventPayload, not arrays
    $payload = EventPayload::fromArray(['test' => 'data']);
    $event = ComponentEvent::broadcast('test', $payload);

    assert($event->payload instanceof EventPayload);
});

// Test 15: Complex nested data
test('Complex: Nested component state', function () {
    $componentId = ComponentId::generate('data-table');

    $data = ComponentData::fromArray([
        'columns' => [
            ['name' => 'id', 'sortable' => true],
            ['name' => 'name', 'sortable' => true],
            ['name' => 'email', 'sortable' => false],
        ],
        'rows' => [
            ['id' => 1, 'name' => 'John', 'email' => 'john@example.com'],
            ['id' => 2, 'name' => 'Jane', 'email' => 'jane@example.com'],
        ],
        'pagination' => [
            'page' => 1,
            'per_page' => 10,
            'total' => 2,
        ],
    ]);

    $columns = $data->getArray('columns');
    assert(count($columns) === 3);

    $pagination = $data->get('pagination');
    assert($pagination['page'] === 1);
    assert($pagination['total'] === 2);
});

echo "\n";
echo "=====================================================\n";
echo "Tests passed: {$testsPassed}\n";
echo "Tests failed: {$testsFailed}\n";
echo "=====================================================\n";

if ($testsFailed > 0) {
    exit(1);
}

echo "\n✅ All LiveComponents integration tests passed!\n";
echo "✅ Type safety refactoring complete!\n";
