<?php
declare(strict_types=1);

namespace PpPhp\Core\Platform\Event\Dispatcher\Execution;

use Throwable;
use PpPhp\Core\Platform\Event\EventBus;
use PpPhp\Core\Platform\Event\Queue\DeferredEvent;
use PpPhp\Core\Platform\Event\Retry\RetryRuntime;
use PpPhp\Core\Platform\Event\Dispatcher\DispatcherResult;

final class DispatcherWorker
{
    public function __construct(
        private readonly EventBus $bus = new EventBus(),
        private readonly RetryRuntime $retry = new RetryRuntime()
    ) {}

    public function handle(DeferredEvent $event, ?callable $handler = null): DispatcherResult
    {
        try {
            if ($handler !== null) {
                $handler($event);
            } else {
                $this->bus->dispatch($event->event());
            }

            return DispatcherResult::success($event->id(), $event->eventName());
        } catch (Throwable $e) {
            $result = $this->retry->execute($event, static function (DeferredEvent $retryEvent) use ($handler): void {
                if ($handler !== null) {
                    $handler($retryEvent);
                    return;
                }
                throw new \RuntimeException('No retry handler available.');
            });

            if ($result->retryScheduled()) {
                return DispatcherResult::retry($event->id(), $event->eventName(), $e->getMessage());
            }

            return DispatcherResult::deadLetter($event->id(), $event->eventName(), $e->getMessage());
        }
    }

    public function retry(): RetryRuntime
    {
        return $this->retry;
    }
}
