<?php
declare(strict_types=1);

namespace PpPhp\Core\Platform\Event;

use PpPhp\Core\Platform\Event\Contracts\EventDispatcherInterface;
use PpPhp\Core\Platform\Event\Contracts\EventInterface;
use PpPhp\Core\Platform\Event\Contracts\EventListenerInterface;
use PpPhp\Core\Platform\Event\Contracts\StoppableEventInterface;
use PpPhp\Core\Platform\Event\Listener\ListenerRepository;
use Throwable;

final class EventDispatcher implements EventDispatcherInterface
{
    public function __construct(private readonly ListenerRepository $listeners) {}

    public function dispatch(EventInterface $event): EventEnvelope
    {
        $envelope = new EventEnvelope($event);

        foreach ($this->listeners->get($event->name()) as $definition) {
            if ($event instanceof StoppableEventInterface && $event->isPropagationStopped()) {
                break;
            }

            $listener = $definition->listener();

            try {
                if ($listener instanceof EventListenerInterface) {
                    $listener->handle($event);
                } else {
                    $listener($event);
                }

                $envelope->markHandled($definition->id());
            } catch (Throwable $e) {
                $envelope->addError($definition->id(), $e->getMessage());
            }
        }

        return $envelope;
    }
}
