<?php

declare(strict_types=1);

use App\Framework\Core\Events\EventDispatcher;
use App\Framework\Core\Events\OnEvent;
use App\Framework\DI\DefaultContainer;

beforeEach(function () {
    $this->container = new DefaultContainer();
    $this->dispatcher = new EventDispatcher($this->container);
});

test('dispatch with no handlers returns empty array', function () {
    $event = new TestEvent('test');
    $results = $this->dispatcher->dispatch($event);

    expect($results)->toBeEmpty();
});

test('null event handlers do not cause errors', function () {
    $dispatcher = new EventDispatcher($this->container, null);
    $event = new TestEvent('test');
    $results = $dispatcher->dispatch($event);

    expect($results)->toBeEmpty();
});

test('manual handler registration works', function () {
    $event = new TestEvent('test message');

    // Register handler manually using the public method
    $this->dispatcher->addHandler(TestEvent::class, function ($event) {
        return 'Manual handler: ' . $event->message;
    });

    $results = $this->dispatcher->dispatch($event);

    expect($results)->toHaveCount(1)
        ->and($results[0])->toBe('Manual handler: test message');
});

test('multiple manual handlers are called', function () {
    $event = new TestEvent('test');

    $this->dispatcher->addHandler(TestEvent::class, function ($event) {
        return 'Handler 1: ' . $event->message;
    });

    $this->dispatcher->addHandler(TestEvent::class, function ($event) {
        return 'Handler 2: ' . $event->message;
    });

    $results = $this->dispatcher->dispatch($event);

    expect($results)->toHaveCount(2)
        ->and($results[0])->toBe('Handler 1: test')
        ->and($results[1])->toBe('Handler 2: test');
});

test('event inheritance works with manual handlers', function () {
    $childEvent = new ChildTestEvent('child message');

    // Register handler for base event
    $this->dispatcher->addHandler(BaseTestEvent::class, function ($event) {
        return 'Base handler: ' . $event->message;
    });

    $results = $this->dispatcher->dispatch($childEvent);

    expect($results)->toHaveCount(1)
        ->and($results[0])->toBe('Base handler: child message');
});

test('class-based handlers work with container', function () {
    $handler = new TestEventHandler();
    $this->container->instance(TestEventHandler::class, $handler);

    $eventHandlers = [
        [
            'event_class' => TestEvent::class,
            'class' => TestEventHandler::class,
            'method' => 'handle',
            'attribute_data' => ['stopPropagation' => false],
        ],
    ];

    $dispatcher = new EventDispatcher($this->container, $eventHandlers);
    $event = new TestEvent('test message');
    $results = $dispatcher->dispatch($event);

    expect($results)->toHaveCount(1)
        ->and($results[0])->toBe('Handled: test message')
        ->and($handler->wasHandled())->toBeTrue();
});

// Test fixtures
class TestEvent
{
    public function __construct(
        public readonly string $message
    ) {
    }
}

class BaseTestEvent
{
    public function __construct(
        public readonly string $message
    ) {
    }
}

class ChildTestEvent extends BaseTestEvent
{
}

class TestEventHandler
{
    private bool $handled = false;

    private int $handleCount = 0;

    #[OnEvent]
    public function handle(TestEvent $event): string
    {
        $this->handled = true;
        $this->handleCount++;

        return 'Handled: ' . $event->message;
    }

    #[OnEvent]
    public function handleBase(BaseTestEvent $event): string
    {
        return 'Base handled: ' . $event->message;
    }

    public function wasHandled(): bool
    {
        return $this->handled;
    }

    public function getHandleCount(): int
    {
        return $this->handleCount;
    }
}

class AnotherTestEventHandler
{
    private bool $processed = false;

    #[OnEvent]
    public function process(TestEvent $event): string
    {
        $this->processed = true;

        return 'Processed: ' . $event->message;
    }

    public function wasProcessed(): bool
    {
        return $this->processed;
    }
}
