package com.airlock.iam.base.infrastructure.event;

import com.airlock.iam.base.api.domain.model.event.DomainEvent;
import com.airlock.iam.base.api.domain.model.event.DomainEventSubscriber;
import com.airlock.iam.base.domain.model.event.EventPublisher;
import com.airlock.iam.elog.api.EDefaultLogType;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Collection;
import java.util.Optional;

public final class EventBus implements EventPublisher {
   private static final ELogger LOG = ELoggerFactory.getLogger(EventBus.class);
   private final Collection<DomainEventSubscriber<?>> domainEventSubscribers;
   private final Optional<EventPublisher> outboxPublisher;

   @Override
   public void publish(DomainEvent event) {
      this.outboxPublisher.ifPresent(outbox -> catchException(() -> outbox.publish(event), "publishing to outbox"));
      this.domainEventSubscribers.forEach(subscriber -> handleEvent(subscriber, event));
   }

   private static void handleEvent(DomainEventSubscriber subscriber, DomainEvent event) {
      if (subscriber.forType().isAssignableFrom(event.getClass())) {
         catchException(() -> subscriber.handleEvent(event), "handling " + event.displayName() + " event in " + subscriber.getClass().getSimpleName());
      }
   }

   private static void catchException(Runnable action, String description) {
      try {
         action.run();
      } catch (Exception e) {
         LOG.warn(EDefaultLogType.GENERIC_LOG_TYPE, e.getClass().getSimpleName() + " while " + description + ": " + e.getMessage(), e);
      }
   }

   @SuppressFBWarnings(justification = "generated code")
   public EventBus(Collection<DomainEventSubscriber<?>> domainEventSubscribers, Optional<EventPublisher> outboxPublisher) {
      this.domainEventSubscribers = domainEventSubscribers;
      this.outboxPublisher = outboxPublisher;
   }
}
