<?php

declare(strict_types=1);

namespace Drupal\flowdrop_interrupt\Service;

use Drupal\flowdrop_interrupt\DTO\ExecutionStamp;
use Drupal\flowdrop_interrupt\Entity\FlowDropInterruptInterface;
use Drupal\flowdrop_interrupt\Message\InterruptMessageInterface;

/**
 * Default {@see InterruptMessageBusInterface} implementation.
 *
 * Handlers self-register through the `flowdrop_interrupt.message_handler`
 * service tag — the container collects them and calls
 * {@see self::addHandler()} on each. Routing is by concrete message class
 * (`get_class($message)`), looked up against
 * {@see InterruptMessageHandlerInterface::supports()}.
 *
 * @internal
 */
final class InterruptMessageBus implements InterruptMessageBusInterface {

  /**
   * Registered handlers keyed by message class.
   *
   * @var array<class-string<InterruptMessageInterface>, InterruptMessageHandlerInterface>
   */
  private array $handlers = [];

  /**
   * {@inheritdoc}
   */
  public function addHandler(InterruptMessageHandlerInterface $handler): void {
    // Duplicate/interface checks are enforced at container build time by
    // InterruptMessageHandlerPass; by the time we get here the wiring is
    // guaranteed valid.
    $this->handlers[$handler::supports()] = $handler;
  }

  /**
   * {@inheritdoc}
   */
  public function dispatch(
    InterruptMessageInterface $message,
    ExecutionStamp $stamp,
  ): FlowDropInterruptInterface {
    $class = $message::class;
    $handler = $this->handlers[$class] ?? NULL;
    if ($handler === NULL) {
      throw new \LogicException(sprintf(
        'No InterruptMessageHandler registered for %s. '
        . 'Define a handler implementing InterruptMessageHandlerInterface '
        . 'and tag the service with `flowdrop_interrupt.message_handler`.',
        $class,
      ));
    }
    return $handler->handle($message, $stamp);
  }

}
